mirror of
https://github.com/ZuneDev/MicrosoftIris.git
synced 2026-07-27 13:13:29 -07:00
802 lines
33 KiB
C#
802 lines
33 KiB
C#
// Decompiled with JetBrains decompiler
|
|
// Type: Microsoft.Iris.Render.Protocol.RenderPort
|
|
// Assembly: UIX.RenderApi, Version=4.8.0.0, Culture=neutral, PublicKeyToken=ddd0da4d3e678217
|
|
// MVID: D47658B8-A8EA-43D6-8837-ECE823BFFFC1
|
|
// Assembly location: C:\Program Files\Zune\UIX.RenderApi.dll
|
|
|
|
using Microsoft.Iris.Render.Common;
|
|
using Microsoft.Iris.Render.Internal;
|
|
using Microsoft.Iris.Render.Protocols.Splash.Messaging;
|
|
using System;
|
|
using System.Collections;
|
|
|
|
namespace Microsoft.Iris.Render.Protocol
|
|
{
|
|
internal class RenderPort : IRenderPortCallback, IRenderHandleOwner
|
|
{
|
|
private const uint c_maxCachedBatchSendInfos = 25;
|
|
private MessagingSession _session;
|
|
private IChannel _channel;
|
|
private ContextID _idRemoteContext;
|
|
private ProtocolSplashMessaging _protocolMessaging;
|
|
private ArrayList _protocols = new ArrayList();
|
|
private HandleTable _handleTable;
|
|
private Map<RENDERGROUP, WeakReference> _groupProxies;
|
|
private MessageCookieLayout _layoutOfRenderHandles;
|
|
private RENDERGROUP _defaultGroupId;
|
|
private RENDERHANDLE _rootHandle;
|
|
private ArrayList _callbackHandlers = new ArrayList();
|
|
private MessageHeap _currentMessageHeap;
|
|
private ObjectCache _spareHeapCache;
|
|
private Map<MessageHeap, MessageHeap> _activeHeaps;
|
|
private uint _nextBatchId;
|
|
private RenderPort.BatchSendInfo _sentBatchesHead;
|
|
private RenderPort.BatchSendInfo _sentBatchesTail;
|
|
private RenderPort.BatchSendInfo _freeBatchSendInfos;
|
|
private uint _numFreeBatchSendInfos;
|
|
private unsafe MessageBatchEntry* _lastBatchEntry;
|
|
private uint _lastBatchBlockId;
|
|
private RENDERHANDLE[] _pendingBatchDeletes;
|
|
private uint _pendingBatchDeleteSlot;
|
|
private RENDERGROUP[] _pendingBatchGroupDeletes;
|
|
private uint _pendingBatchGroupDeleteSlot;
|
|
private BatchCallback _batchPopulateCallback;
|
|
private bool _individualMessageMode;
|
|
private uint _uBatchedMessages;
|
|
private DeferredHandler _deferredObjectReleaseWorker;
|
|
private DeferredHandler _deferredGroupReleaseWorker;
|
|
private DeferredHandler _deferredHeapReleaseWorker;
|
|
private DeferredHandler _deferredTrafficUpdate;
|
|
private bool _foreignByteOrder;
|
|
private static object s_SyncLock = new object();
|
|
private static ushort[] s_ByteOrder_CallbackMessage;
|
|
private static ushort[] s_ByteOrder_MessageBatchHeader;
|
|
private static ushort[] s_ByteOrder_MessageBatchEntry;
|
|
private ulong _ulOverallControlTraffic;
|
|
private uint _uRecentControlTraffic;
|
|
private ulong _ulOverallDataTraffic;
|
|
private uint _uRecentDataTraffic;
|
|
private ulong _ulOverallTotalTraffic;
|
|
private DateTime _dtRecentTrafficTime;
|
|
private bool _fPendingTrafficSummary;
|
|
private static TimeSpan s_timeTrafficWindow = TimeSpan.FromMilliseconds(500.0);
|
|
|
|
internal RenderPort(
|
|
MessagingSession session,
|
|
IChannel channel,
|
|
ContextID idRemoteContext,
|
|
RENDERGROUP idGroup,
|
|
MessageCookieLayout layout,
|
|
bool foreignByteOrder)
|
|
{
|
|
Debug2.Validate(session != null, typeof(ArgumentNullException), nameof(session));
|
|
Debug2.Validate(channel != null, typeof(ArgumentNullException), nameof(channel));
|
|
Debug2.Validate(idRemoteContext != ContextID.NULL, typeof(ArgumentNullException), nameof(idRemoteContext));
|
|
this._deferredObjectReleaseWorker = new DeferredHandler(this.DeferredObjectReleaseWorker);
|
|
this._deferredGroupReleaseWorker = new DeferredHandler(this.DeferredGroupReleaseWorker);
|
|
this._deferredHeapReleaseWorker = new DeferredHandler(this.DeferredHeapReleaseWorker);
|
|
this._deferredTrafficUpdate = new DeferredHandler(this.DeferredTrafficUpdate);
|
|
this._activeHeaps = new Map<MessageHeap, MessageHeap>();
|
|
this._spareHeapCache = new ObjectCache(new ObjectCache.Callback(this.HeapCacheCallback), 10, false);
|
|
this._individualMessageMode = true;
|
|
this._layoutOfRenderHandles = layout;
|
|
this._handleTable = new HandleTable(this, this._layoutOfRenderHandles);
|
|
this._defaultGroupId = this.AllocHandleGroup(false);
|
|
this._rootHandle = this.AllocHandle(this);
|
|
if (idGroup != RENDERGROUP.NULL)
|
|
{
|
|
this.AllocHandleGroupWithId(idGroup);
|
|
this._defaultGroupId = idGroup;
|
|
}
|
|
this._channel = channel;
|
|
this._session = session;
|
|
this._foreignByteOrder = foreignByteOrder;
|
|
this._idRemoteContext = idRemoteContext;
|
|
this._protocolMessaging = ProtocolSplashMessaging.Bind(this);
|
|
}
|
|
|
|
public void Connect()
|
|
{
|
|
Debug2.Throw(this._handleTable != null && this._channel != null, "must be valid to connect");
|
|
Debug2.Throw(!this._channel.IsConnected, "already connected");
|
|
this._channel.Connect(this._idRemoteContext, this._rootHandle, this._layoutOfRenderHandles);
|
|
foreach (ProtocolInstance protocol in this._protocols)
|
|
protocol.OnConnect();
|
|
}
|
|
|
|
internal void PingConnection() => LocalRenderPortCallback.AutoSendOnPingReply(this._protocolMessaging, RENDERHANDLE.NULL);
|
|
|
|
internal event EventHandler PingReply;
|
|
|
|
public void Dispose()
|
|
{
|
|
this.PingReply = null;
|
|
this.DisposeObjectMaps();
|
|
if (this._channel != null)
|
|
{
|
|
this._channel.Dispose();
|
|
this._channel = null;
|
|
}
|
|
if (this._spareHeapCache == null)
|
|
return;
|
|
if (this._currentMessageHeap != null)
|
|
this.ReleaseHeap(this._currentMessageHeap);
|
|
this._currentMessageHeap = null;
|
|
this._spareHeapCache.Dispose();
|
|
this._spareHeapCache = null;
|
|
}
|
|
|
|
public void DisposeObjectMaps()
|
|
{
|
|
if (this._handleTable == null)
|
|
return;
|
|
this._handleTable.Dispose();
|
|
this._handleTable = null;
|
|
}
|
|
|
|
public MessagingSession Session => this._session;
|
|
|
|
public ContextID RemoteContext => this._idRemoteContext;
|
|
|
|
public bool ForeignByteOrder => this._foreignByteOrder;
|
|
|
|
public bool AlwaysBigEndian => this._foreignByteOrder;
|
|
|
|
internal uint DefaultGroupID => RENDERGROUP.ToUInt32(this._defaultGroupId);
|
|
|
|
RENDERHANDLE IRenderHandleOwner.RenderHandle => RENDERHANDLE.NULL;
|
|
|
|
void IRenderHandleOwner.OnDisconnect()
|
|
{
|
|
}
|
|
|
|
internal ProtocolSplashMessaging MessagingProtocol => this._protocolMessaging;
|
|
|
|
public ProtocolInstance LookUpProtocol(string name)
|
|
{
|
|
ProtocolInstance protocolInstance = null;
|
|
foreach (ProtocolInstance protocol in this._protocols)
|
|
{
|
|
if (protocol.Name == name)
|
|
protocolInstance = protocol;
|
|
}
|
|
return protocolInstance;
|
|
}
|
|
|
|
internal void BindProtocol(ProtocolInstance protocol)
|
|
{
|
|
Debug2.Validate(this.LookUpProtocol(protocol.Name) == null, typeof(InvalidOperationException), nameof(protocol), "protocol already bound");
|
|
this._protocols.Add(protocol);
|
|
if (this._channel == null || !this._channel.IsConnected)
|
|
return;
|
|
protocol.OnConnect();
|
|
}
|
|
|
|
internal RENDERHANDLE InitRemoteClass(string className)
|
|
{
|
|
if (className.Equals("Splash::Messaging::Broker"))
|
|
return this._rootHandle;
|
|
RENDERHANDLE idObjectClass = this.AllocHandle(this);
|
|
RemoteBroker.SendCreateClass(this._protocolMessaging, className, idObjectClass);
|
|
return idObjectClass;
|
|
}
|
|
|
|
internal unsafe void CreateRemoteObject(
|
|
RENDERHANDLE classCreate,
|
|
RENDERHANDLE hNewObject,
|
|
Message* pmsgConstructor)
|
|
{
|
|
RemoteBroker.SendCreateObject(this._protocolMessaging, classCreate, hNewObject, pmsgConstructor);
|
|
}
|
|
|
|
internal unsafe void SendLocalCallbackMessage(Message* pmsgCallback) => Protocols.Splash.Messaging.RemoteContext.SendForwardMessage(this._protocolMessaging, this._session.LocalContext, pmsgCallback);
|
|
|
|
internal void LinkContext(ContextID idContextExisting, ContextID idContextAlias) => RemoteContextRelay.SendLinkContext(this._protocolMessaging, idContextExisting, idContextAlias);
|
|
|
|
internal void UnlinkContext(ContextID idContextExisting, ContextID idContextAlias) => RemoteContextRelay.SendUnlinkContext(this._protocolMessaging, idContextExisting, idContextAlias);
|
|
|
|
public void BeginMessageBatch(BatchCallback populateCallback)
|
|
{
|
|
this.Session.AssertOwningThread();
|
|
Debug2.Throw(this._individualMessageMode, "Already in batch mode");
|
|
this._batchPopulateCallback = populateCallback;
|
|
this._individualMessageMode = false;
|
|
}
|
|
|
|
public void EndMessageBatch(BatchCallback deliveryCallback)
|
|
{
|
|
this.Session.AssertOwningThread();
|
|
Debug2.Throw(!this._individualMessageMode, "Must be collecting a batch to end");
|
|
this.SendCurrentBatch(deliveryCallback);
|
|
this._individualMessageMode = true;
|
|
}
|
|
|
|
public unsafe void SendDataBuffer(void* pvData, uint cbSize, RENDERHANDLE idRemoteBuffer)
|
|
{
|
|
Debug2.Validate(idRemoteBuffer != RENDERHANDLE.NULL, typeof(ArgumentNullException), nameof(idRemoteBuffer));
|
|
this.UpdateTraffic(0U, cbSize);
|
|
var info = new EngineApi.BufferInfo
|
|
{
|
|
idContextSrc = this._session.LocalContext,
|
|
idContextDest = this._idRemoteContext,
|
|
idBuffer = idRemoteBuffer,
|
|
nFlags = 0,
|
|
cbSizeBuffer = cbSize
|
|
};
|
|
EngineApi.IFC(EngineApi.SpBufferOpen(&info, pvData));
|
|
}
|
|
|
|
public unsafe void SendBatchBuffer(void* pvData, uint cbSize, RENDERHANDLE idRemoteBuffer)
|
|
{
|
|
int num = idRemoteBuffer != RENDERHANDLE.NULL ? 1 : 0;
|
|
this.UpdateTraffic(cbSize, 0U);
|
|
var info = new EngineApi.BufferInfo()
|
|
{
|
|
idContextSrc = this._session.LocalContext,
|
|
idContextDest = this._idRemoteContext,
|
|
idBuffer = idRemoteBuffer,
|
|
nFlags = EngineApi.BufferFlags.IsBatch,
|
|
cbSizeBuffer = cbSize
|
|
};
|
|
EngineApi.IFC(EngineApi.SpBufferOpen(&info, pvData));
|
|
}
|
|
|
|
public unsafe void SendMessageBuffer(Message* pmsg)
|
|
{
|
|
this.UpdateTraffic(pmsg->cbSize, 0U);
|
|
uint src = pmsg->cbSize;
|
|
if (this.ForeignByteOrder)
|
|
src = Memory.ConvertEndian(src);
|
|
var info = new EngineApi.BufferInfo()
|
|
{
|
|
idContextSrc = this._session.LocalContext,
|
|
idContextDest = this._idRemoteContext,
|
|
idBuffer = RENDERHANDLE.NULL,
|
|
nFlags = EngineApi.BufferFlags.CopyData,
|
|
cbSizeBuffer = src
|
|
};
|
|
EngineApi.IFC(EngineApi.SpBufferOpen(&info, pmsg));
|
|
}
|
|
|
|
private void UpdateTraffic(uint uControl, uint uData)
|
|
{
|
|
this._uRecentControlTraffic += uControl;
|
|
this._uRecentDataTraffic += uData;
|
|
this._dtRecentTrafficTime = DateTime.UtcNow;
|
|
if (Debug2.IsCategoryEnabled(DebugCategory.Protocol, 1))
|
|
{
|
|
if (this._fPendingTrafficSummary || !this.Session.IsConnected)
|
|
return;
|
|
this.Session.DeferredInvoke(_deferredTrafficUpdate, null, DeferredInvokePriority.Idle, s_timeTrafficWindow);
|
|
this._fPendingTrafficSummary = true;
|
|
}
|
|
else
|
|
this.TrafficUpdateWorker();
|
|
}
|
|
|
|
private void DeferredTrafficUpdate(object obj)
|
|
{
|
|
if (!this._fPendingTrafficSummary)
|
|
return;
|
|
if (DateTime.UtcNow - this._dtRecentTrafficTime < s_timeTrafficWindow)
|
|
{
|
|
this.Session.DeferredInvoke(_deferredTrafficUpdate, null, DeferredInvokePriority.Idle, this._dtRecentTrafficTime + s_timeTrafficWindow - DateTime.UtcNow);
|
|
}
|
|
else
|
|
{
|
|
this._fPendingTrafficSummary = false;
|
|
this.TrafficUpdateWorker();
|
|
}
|
|
}
|
|
|
|
private void TrafficUpdateWorker()
|
|
{
|
|
this._ulOverallControlTraffic += _uRecentControlTraffic;
|
|
this._ulOverallDataTraffic += _uRecentDataTraffic;
|
|
this._ulOverallTotalTraffic += this._uRecentControlTraffic + this._uRecentDataTraffic;
|
|
Debug2.IsCategoryEnabled(DebugCategory.Protocol, 1);
|
|
this._uRecentControlTraffic = 0U;
|
|
this._uRecentDataTraffic = 0U;
|
|
}
|
|
|
|
public unsafe void* AllocMessageBuffer(uint size)
|
|
{
|
|
this.Session.AssertOwningThread();
|
|
if (this._currentMessageHeap == null)
|
|
this._currentMessageHeap = this.AllocHeap();
|
|
return this._currentMessageHeap.Alloc(size);
|
|
}
|
|
|
|
public unsafe void SendRemoteMessage(Message* pmsg)
|
|
{
|
|
this.Session.AssertOwningThread();
|
|
Debug2.Throw(this._channel != null, "Not connected to renderer");
|
|
if (this._individualMessageMode)
|
|
{
|
|
this.SendMessageBuffer(pmsg);
|
|
if (this._currentMessageHeap == null)
|
|
return;
|
|
this._currentMessageHeap.Reset();
|
|
}
|
|
else
|
|
this.StoreMessageEntry(pmsg);
|
|
}
|
|
|
|
public RENDERHANDLE AllocHandle(IRenderHandleOwner owner)
|
|
{
|
|
Debug2.Throw(this._handleTable != null, "Render port has already shut down");
|
|
Debug2.Validate(owner != null, typeof(ArgumentNullException), nameof(owner));
|
|
return this._handleTable.Alloc(this._defaultGroupId, owner);
|
|
}
|
|
|
|
public void RegisterKnownHandle(IRenderHandleOwner owner, RENDERHANDLE handle)
|
|
{
|
|
Debug2.Throw(this._handleTable != null, "Render port has already shut down");
|
|
Debug2.Validate(owner != null, typeof(ArgumentNullException), nameof(owner));
|
|
this._handleTable.AllocWithId(owner, handle, true);
|
|
}
|
|
|
|
public void FreeHandle(RENDERHANDLE handle)
|
|
{
|
|
if (this.Session.IsOwningThread())
|
|
{
|
|
if (this._handleTable == null)
|
|
return;
|
|
if (this._channel != null && this._handleTable.GetGroup(handle) == this._defaultGroupId)
|
|
{
|
|
this._handleTable.SetOwner(handle, null);
|
|
this.AddPendingDelete(handle);
|
|
RemoteBroker.SendDestroyObject(this._protocolMessaging, handle);
|
|
}
|
|
else
|
|
this._handleTable.Free(handle);
|
|
}
|
|
else
|
|
this.Session.DeferredInvoke(_deferredObjectReleaseWorker, handle, DeferredInvokePriority.High);
|
|
}
|
|
|
|
public void CancelAllocHandle(RENDERHANDLE handle)
|
|
{
|
|
if (this._handleTable == null)
|
|
return;
|
|
this._handleTable.Free(handle);
|
|
}
|
|
|
|
public IRenderHandleOwner TryGetHandleOwner(RENDERHANDLE handle) => this._handleTable.GetOwner(handle, true);
|
|
|
|
public IRenderHandleOwner TryGetProxyOwner(RENDERHANDLE handle)
|
|
{
|
|
IRenderHandleOwner renderHandleOwner = null;
|
|
if (this._groupProxies != null)
|
|
{
|
|
RENDERGROUP group = this._handleTable.GetGroup(handle);
|
|
if (group != RENDERGROUP.NULL)
|
|
renderHandleOwner = this.GetGroupProxy(group);
|
|
}
|
|
return renderHandleOwner;
|
|
}
|
|
|
|
public IRenderHandleOwner GetHandleOwner(RENDERHANDLE handle) => this._handleTable.GetOwner(handle);
|
|
|
|
public void ValidateHandle(RENDERHANDLE handle) => this._handleTable.Validate(handle);
|
|
|
|
public void ValidateHandleOrNull(RENDERHANDLE handle)
|
|
{
|
|
if (!(handle != RENDERHANDLE.NULL))
|
|
return;
|
|
this._handleTable.Validate(handle);
|
|
}
|
|
|
|
public RENDERGROUP AllocHandleGroup() => this.AllocHandleGroup(true);
|
|
|
|
public void AllocHandleGroupWithId(RENDERGROUP idGroup) => this._handleTable.AllocGroupWithId(idGroup);
|
|
|
|
private RENDERGROUP AllocHandleGroup(bool failIfNotConnected)
|
|
{
|
|
RENDERGROUP handle = this._handleTable.AllocGroup();
|
|
if (this._channel != null)
|
|
Protocols.Splash.Messaging.RemoteContext.SendCreateGroup(this._protocolMessaging, (int)RENDERGROUP.ToUInt32(handle), this._session.LocalContext);
|
|
else if (failIfNotConnected)
|
|
Debug2.Throw(false, "Not connected to renderer");
|
|
return handle;
|
|
}
|
|
|
|
public void FreeHandleGroup(RENDERGROUP group)
|
|
{
|
|
if (this.Session.IsOwningThread())
|
|
{
|
|
if (this._handleTable == null)
|
|
return;
|
|
if (this._channel != null)
|
|
{
|
|
this.AddPendingGroupDelete(group);
|
|
Protocols.Splash.Messaging.RemoteContext.SendDestroyGroup(this._protocolMessaging, (int)RENDERGROUP.ToUInt32(group));
|
|
}
|
|
else
|
|
this._handleTable.FreeGroup(group);
|
|
}
|
|
else
|
|
this.Session.DeferredInvoke(_deferredGroupReleaseWorker, group, DeferredInvokePriority.High);
|
|
}
|
|
|
|
public void RegisterGroupProxy(RENDERGROUP group, IRenderHandleOwner proxy)
|
|
{
|
|
if (this._groupProxies == null)
|
|
this._groupProxies = new Map<RENDERGROUP, WeakReference>();
|
|
this._groupProxies[group] = new WeakReference(proxy);
|
|
}
|
|
|
|
public void RevokeGroupProxy(RENDERGROUP group)
|
|
{
|
|
if (this._groupProxies == null)
|
|
return;
|
|
this._groupProxies[group] = null;
|
|
}
|
|
|
|
public IRenderHandleOwner GetGroupProxy(RENDERGROUP group)
|
|
{
|
|
IRenderHandleOwner renderHandleOwner = null;
|
|
if (this._groupProxies != null)
|
|
{
|
|
WeakReference groupProxy = this._groupProxies[group];
|
|
if (groupProxy != null)
|
|
renderHandleOwner = groupProxy.Target as IRenderHandleOwner;
|
|
}
|
|
return renderHandleOwner;
|
|
}
|
|
|
|
public MessageHeap AllocHeap()
|
|
{
|
|
this.Session.AssertOwningThread();
|
|
return (MessageHeap)this._spareHeapCache.Pop();
|
|
}
|
|
|
|
public void ReleaseHeap(MessageHeap heap)
|
|
{
|
|
if (this.Session.IsOwningThread())
|
|
this.DeferredHeapReleaseWorker(heap);
|
|
else if (this.Session.OwningThread != null)
|
|
this.Session.DeferredInvoke(_deferredHeapReleaseWorker, heap, DeferredInvokePriority.High);
|
|
else
|
|
heap.Dispose();
|
|
}
|
|
|
|
private unsafe void StoreMessageEntry(Message* pMethodMsg)
|
|
{
|
|
MessageHeap.Block block = this._currentMessageHeap.LookupBlock(pMethodMsg);
|
|
Debug2.Throw((IntPtr)block.data != IntPtr.Zero, "message should be in current message heap");
|
|
uint num = (uint)((ulong)((sbyte*)pMethodMsg - (sbyte*)block.data) - (ulong)sizeof(MessageBatchEntry));
|
|
if ((IntPtr)_lastBatchEntry != IntPtr.Zero && _lastBatchBlockId == block.id)
|
|
this._lastBatchEntry->uOffsetNextEntry = num;
|
|
else
|
|
((MessageBatchHeader*)block.data)->uOffsetFirstEntry = num;
|
|
this._lastBatchEntry = (MessageBatchEntry*)((nint)block.data + num);
|
|
this._lastBatchBlockId = block.id;
|
|
++this._uBatchedMessages;
|
|
BatchCallback populateCallback = this._batchPopulateCallback;
|
|
if (populateCallback == null)
|
|
return;
|
|
this._batchPopulateCallback = null;
|
|
populateCallback();
|
|
}
|
|
|
|
private unsafe void SendCurrentBatch(BatchCallback deliveryCallback)
|
|
{
|
|
try
|
|
{
|
|
if ((IntPtr)this._lastBatchEntry != IntPtr.Zero)
|
|
{
|
|
uint nextBatchId = this._nextBatchId;
|
|
++this._nextBatchId;
|
|
try
|
|
{
|
|
LocalRenderPortCallback.AutoSendOnBatchProcessed(this._protocolMessaging, RENDERHANDLE.NULL, nextBatchId);
|
|
RenderPort.BatchSendInfo info = this.AllocBatchSendInfo();
|
|
info.batchID = nextBatchId;
|
|
info.heap = this._currentMessageHeap;
|
|
info.pendingDeletes = this._pendingBatchDeletes;
|
|
info.pendingGroupDeletes = this._pendingBatchGroupDeletes;
|
|
info.deliveryCallback = deliveryCallback;
|
|
this.EnqueuePendingBatch(info);
|
|
RENDERHANDLE renderhandle = RENDERHANDLE.NULL;
|
|
int blockCount = this._currentMessageHeap.BlockCount;
|
|
uint num = 0;
|
|
MessageHeap.Enumerator enumerator = this._currentMessageHeap.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
MessageHeap.Block current = enumerator.Current;
|
|
MessageBatchHeader* data = (MessageBatchHeader*)current.data;
|
|
RENDERHANDLE idRemoteBuffer = RENDERHANDLE.NULL;
|
|
--blockCount;
|
|
if (blockCount > 0)
|
|
idRemoteBuffer = this.AllocHandle(this);
|
|
data->idPredicateBuffer = renderhandle;
|
|
if (this.ForeignByteOrder)
|
|
SwapBatchListByteOrder(data);
|
|
num += current.size;
|
|
this.SendBatchBuffer(current.data, current.size, idRemoteBuffer);
|
|
renderhandle = idRemoteBuffer;
|
|
}
|
|
this._currentMessageHeap = null;
|
|
this._pendingBatchDeletes = null;
|
|
this._pendingBatchDeleteSlot = 0U;
|
|
this._pendingBatchGroupDeletes = null;
|
|
this._pendingBatchGroupDeleteSlot = 0U;
|
|
}
|
|
finally
|
|
{
|
|
if (this._currentMessageHeap != null)
|
|
{
|
|
this.DequeuePendingBatch(nextBatchId, true);
|
|
this.ReleaseHeap(this._currentMessageHeap);
|
|
this._currentMessageHeap = null;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (this._currentMessageHeap != null)
|
|
{
|
|
this.ReleaseHeap(this._currentMessageHeap);
|
|
this._currentMessageHeap = null;
|
|
}
|
|
if (deliveryCallback == null)
|
|
return;
|
|
deliveryCallback();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
this._currentMessageHeap = null;
|
|
this._batchPopulateCallback = null;
|
|
this._lastBatchEntry = null;
|
|
this._lastBatchBlockId = 0U;
|
|
this._uBatchedMessages = 0U;
|
|
}
|
|
}
|
|
|
|
private static unsafe void SwapBatchListByteOrder(MessageBatchHeader* pHeader)
|
|
{
|
|
byte* pMem1 = (byte*)pHeader;
|
|
uint num = pHeader->uOffsetFirstEntry;
|
|
MarshalHelper.SwapByteOrder(pMem1, ref s_ByteOrder_MessageBatchHeader, typeof(MessageBatchHeader), 0, 0);
|
|
while (num != 0U)
|
|
{
|
|
byte* pMem2 = pMem1 + (int)num;
|
|
num = ((MessageBatchEntry*)pMem2)->uOffsetNextEntry;
|
|
MarshalHelper.SwapByteOrder(pMem2, ref s_ByteOrder_MessageBatchEntry, typeof(MessageBatchEntry), 0, 0);
|
|
}
|
|
}
|
|
|
|
private void DeferredObjectReleaseWorker(object args) => this.FreeHandle((RENDERHANDLE)args);
|
|
|
|
private void DeferredGroupReleaseWorker(object args) => this.FreeHandleGroup((RENDERGROUP)args);
|
|
|
|
private void DeferredHeapReleaseWorker(object args) => this._spareHeapCache.Push((MessageHeap)args);
|
|
|
|
private unsafe object HeapCacheCallback(ObjectCache.Operation operation, object data)
|
|
{
|
|
this.Session.AssertOwningThread();
|
|
MessageHeap key = data as MessageHeap;
|
|
switch (operation)
|
|
{
|
|
case ObjectCache.Operation.Alloc:
|
|
key = new MessageHeap(8U * Win32Api.GetSystemPageSize(), (uint)sizeof(MessageBatchHeader), (uint)sizeof(MessageBatchEntry));
|
|
this._activeHeaps[key] = key;
|
|
break;
|
|
case ObjectCache.Operation.Free:
|
|
this._activeHeaps.Remove(key);
|
|
key.Dispose();
|
|
key = null;
|
|
break;
|
|
case ObjectCache.Operation.Freeze:
|
|
Debug2.Validate(key != null, typeof(ArgumentNullException), nameof(data));
|
|
key.Reset();
|
|
break;
|
|
}
|
|
return key;
|
|
}
|
|
|
|
internal unsafe void ProcessMessageBuffer(EngineApi.BufferInfo* pBufferInfo, void* pvBufferData)
|
|
{
|
|
if (this.ForeignByteOrder)
|
|
MarshalHelper.SwapByteOrder((byte*)pvBufferData, ref s_ByteOrder_CallbackMessage, typeof(CallbackMessage), 0, 0);
|
|
CallbackMessage* message = (CallbackMessage*)pvBufferData;
|
|
this.DispatchCallbackMessage(RENDERHANDLE.ToUInt32(message->idObjectSubject), message);
|
|
}
|
|
|
|
internal RENDERHANDLE RegisterCallback(PortCallback callback, out uint idCallback)
|
|
{
|
|
idCallback = (uint)this._callbackHandlers.Add(callback);
|
|
return RENDERHANDLE.FromUInt32(idCallback + 2U);
|
|
}
|
|
|
|
private unsafe void DispatchCallbackMessage(uint idCallback, CallbackMessage* message)
|
|
{
|
|
idCallback -= 2U;
|
|
if (idCallback >= (uint)this._callbackHandlers.Count)
|
|
return;
|
|
PortCallback callbackHandler = (PortCallback)this._callbackHandlers[(int)idCallback];
|
|
if (callbackHandler == null)
|
|
return;
|
|
IRenderHandleOwner owner = this;
|
|
if (message->hTarget != RENDERHANDLE.NULL)
|
|
{
|
|
if (this._handleTable == null)
|
|
return;
|
|
owner = this.TryGetHandleOwner(message->hTarget);
|
|
if (owner == null)
|
|
return;
|
|
}
|
|
callbackHandler(this, owner, message);
|
|
}
|
|
|
|
void IRenderPortCallback.OnPingReply(RENDERHANDLE target)
|
|
{
|
|
if (this.PingReply == null)
|
|
return;
|
|
this.PingReply(this, EventArgs.Empty);
|
|
}
|
|
|
|
void IRenderPortCallback.OnBatchProcessed(
|
|
RENDERHANDLE target,
|
|
uint uBatchCompleted)
|
|
{
|
|
RenderPort.BatchSendInfo info = this.DequeuePendingBatch(uBatchCompleted, false);
|
|
if (info == null)
|
|
return;
|
|
this.ProcessPendingDeletes(info.pendingDeletes);
|
|
this.ProcessPendingGroupDeletes(info.pendingGroupDeletes);
|
|
this.ReleaseHeap(info.heap);
|
|
if (info.deliveryCallback != null)
|
|
info.deliveryCallback();
|
|
this.FreeBatchSendInfo(info);
|
|
}
|
|
|
|
private void AddPendingDelete(RENDERHANDLE handle)
|
|
{
|
|
uint num1 = 0;
|
|
if (this._pendingBatchDeletes != null)
|
|
num1 = (uint)this._pendingBatchDeletes.Length;
|
|
if (this._pendingBatchDeleteSlot >= num1)
|
|
{
|
|
uint num2 = 2U * num1;
|
|
if (num2 == 0U)
|
|
num2 = 16U;
|
|
RENDERHANDLE[] renderhandleArray = new RENDERHANDLE[num2];
|
|
if (num1 > 0U)
|
|
Array.Copy(_pendingBatchDeletes, renderhandleArray, this._pendingBatchDeletes.Length);
|
|
this._pendingBatchDeletes = renderhandleArray;
|
|
}
|
|
this._pendingBatchDeletes[this._pendingBatchDeleteSlot] = handle;
|
|
++this._pendingBatchDeleteSlot;
|
|
}
|
|
|
|
private void ProcessPendingDeletes(RENDERHANDLE[] deleteList)
|
|
{
|
|
if (deleteList == null || this._handleTable == null)
|
|
return;
|
|
foreach (RENDERHANDLE delete in deleteList)
|
|
{
|
|
if (delete != RENDERHANDLE.NULL)
|
|
this._handleTable.Free(delete);
|
|
}
|
|
}
|
|
|
|
private void AddPendingGroupDelete(RENDERGROUP group)
|
|
{
|
|
uint num1 = 0;
|
|
if (this._pendingBatchGroupDeletes != null)
|
|
num1 = (uint)this._pendingBatchGroupDeletes.Length;
|
|
if (this._pendingBatchGroupDeleteSlot >= num1)
|
|
{
|
|
uint num2 = 2U * num1;
|
|
if (num2 == 0U)
|
|
num2 = 16U;
|
|
RENDERGROUP[] rendergroupArray = new RENDERGROUP[num2];
|
|
if (num1 > 0U)
|
|
Array.Copy(_pendingBatchGroupDeletes, rendergroupArray, this._pendingBatchGroupDeletes.Length);
|
|
this._pendingBatchGroupDeletes = rendergroupArray;
|
|
}
|
|
this._pendingBatchGroupDeletes[this._pendingBatchGroupDeleteSlot] = group;
|
|
++this._pendingBatchGroupDeleteSlot;
|
|
}
|
|
|
|
private void ProcessPendingGroupDeletes(RENDERGROUP[] deleteList)
|
|
{
|
|
if (deleteList == null || this._handleTable == null)
|
|
return;
|
|
foreach (RENDERGROUP delete in deleteList)
|
|
{
|
|
if (delete != RENDERGROUP.NULL)
|
|
this._handleTable.FreeGroup(delete);
|
|
}
|
|
}
|
|
|
|
private RenderPort.BatchSendInfo AllocBatchSendInfo()
|
|
{
|
|
RenderPort.BatchSendInfo batchSendInfo;
|
|
if (this._freeBatchSendInfos != null)
|
|
{
|
|
batchSendInfo = this._freeBatchSendInfos;
|
|
this._freeBatchSendInfos = batchSendInfo.next;
|
|
batchSendInfo.next = null;
|
|
--this._numFreeBatchSendInfos;
|
|
}
|
|
else
|
|
batchSendInfo = new RenderPort.BatchSendInfo();
|
|
return batchSendInfo;
|
|
}
|
|
|
|
private void FreeBatchSendInfo(RenderPort.BatchSendInfo info)
|
|
{
|
|
info.Reset();
|
|
if (this._numFreeBatchSendInfos >= 25U)
|
|
return;
|
|
info.next = this._freeBatchSendInfos;
|
|
this._freeBatchSendInfos = info;
|
|
++this._numFreeBatchSendInfos;
|
|
}
|
|
|
|
private void EnqueuePendingBatch(RenderPort.BatchSendInfo info)
|
|
{
|
|
if (this._sentBatchesTail == null)
|
|
{
|
|
this._sentBatchesHead = info;
|
|
this._sentBatchesTail = info;
|
|
}
|
|
else
|
|
{
|
|
this._sentBatchesTail.next = info;
|
|
this._sentBatchesTail = info;
|
|
}
|
|
}
|
|
|
|
private RenderPort.BatchSendInfo DequeuePendingBatch(uint batchID, bool fFreeInfo)
|
|
{
|
|
RenderPort.BatchSendInfo batchSendInfo = null;
|
|
RenderPort.BatchSendInfo info;
|
|
for (info = this._sentBatchesHead; info != null && (int)info.batchID != (int)batchID; info = info.next)
|
|
batchSendInfo = info;
|
|
if (info != null)
|
|
{
|
|
if (this._sentBatchesTail == info)
|
|
this._sentBatchesTail = batchSendInfo;
|
|
if (batchSendInfo == null)
|
|
this._sentBatchesHead = info.next;
|
|
else
|
|
batchSendInfo.next = info.next;
|
|
info.next = null;
|
|
if (fFreeInfo)
|
|
{
|
|
this.FreeBatchSendInfo(info);
|
|
info = null;
|
|
}
|
|
}
|
|
return info;
|
|
}
|
|
|
|
public static object SyncLock => s_SyncLock;
|
|
|
|
private class BatchSendInfo
|
|
{
|
|
public uint batchID;
|
|
public MessageHeap heap;
|
|
public RENDERHANDLE[] pendingDeletes;
|
|
public RENDERGROUP[] pendingGroupDeletes;
|
|
public BatchCallback deliveryCallback;
|
|
public RenderPort.BatchSendInfo next;
|
|
|
|
public void Reset()
|
|
{
|
|
this.batchID = 0U;
|
|
this.heap = null;
|
|
this.pendingDeletes = null;
|
|
this.pendingGroupDeletes = null;
|
|
this.deliveryCallback = null;
|
|
this.next = null;
|
|
}
|
|
}
|
|
}
|
|
}
|