mirror of
https://github.com/ZuneDev/MicrosoftIris.git
synced 2026-07-27 13:13:29 -07:00
Empty UIXrender and UIXsup, retry impl
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.Iris.Render.Interop;
|
||||
|
||||
namespace Microsoft.Iris.Render.Engine;
|
||||
|
||||
// A custom (non-generic) delegate so ReadOnlySpan<byte> is legal here -- Span<T> can't
|
||||
// be an Action<T>/Func<T> type argument (ref structs aren't valid generic arguments),
|
||||
// but a hand-declared delegate type can take one directly.
|
||||
public delegate void BufferReceivedHandler(ContextID sourceContext, RENDERHANDLE bufferHandle, BufferFlags flags, ReadOnlySpan<byte> data);
|
||||
@@ -1,21 +0,0 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Microsoft.Iris.Render.Interop;
|
||||
|
||||
namespace Microsoft.Iris.Render.Engine;
|
||||
|
||||
// Maps a registered ContextID to the handler it should receive deliveries on via
|
||||
// SendBuffer -- the smallest coherent slice of "Engine core" (see
|
||||
// logs/UIXrender/EngineCore.md) needed for StartRenderThread and SendBuffer to mean
|
||||
// anything together. Holds an ordinary delegate now (not a raw callback pointer) --
|
||||
// pointer marshaling, where it's still needed for native callers, lives entirely in
|
||||
// Interop/EngineApi.cs.
|
||||
internal static class ContextRegistry
|
||||
{
|
||||
private static readonly ConcurrentDictionary<uint, BufferReceivedHandler> s_contexts = new();
|
||||
|
||||
public static void Register(ContextID id, BufferReceivedHandler handler) => s_contexts[id.value] = handler;
|
||||
|
||||
public static void Unregister(ContextID id) => s_contexts.TryRemove(id.value, out _);
|
||||
|
||||
public static bool TryGet(ContextID id, out BufferReceivedHandler handler) => s_contexts.TryGetValue(id.value, out handler);
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.Iris.Render.Interop;
|
||||
using Microsoft.Iris.Render.Interop.Protocol;
|
||||
using Microsoft.Iris.Render.Subsystems.Os;
|
||||
using Microsoft.Iris.Render.Subsystems.Remote;
|
||||
|
||||
namespace Microsoft.Iris.Render.Engine;
|
||||
|
||||
// The idiomatic managed API for UIXrender's core transport -- the single thing both
|
||||
// Interop/EngineApi.cs's [UnmanagedCallersOnly] shims (for native callers) and managed
|
||||
// callers like UIX.RenderApi call into, so there is exactly one implementation of each
|
||||
// operation. The buffer-delivery path (StartRenderThread/SendBuffer) is entirely
|
||||
// pointer-free. The remainder (Invoke, the SpRemote* family, ObjectRelease) deal in
|
||||
// opaque IntPtr *handles* the caller round-trips back to us -- never dereferenced here --
|
||||
// plus, for Invoke only, a raw function pointer the caller asked us to run; that one spot
|
||||
// is the sole `unsafe` in this type. See logs/UIXrender/EngineCore.md and FullSurface.md.
|
||||
public static class EngineService
|
||||
{
|
||||
// ---- buffer delivery (pointer-free) ----------------------------------------------
|
||||
|
||||
public static IRenderThreadHandle StartRenderThread(ContextID contextId, BufferReceivedHandler onBufferReceived)
|
||||
=> RenderThread.Start(contextId, onBufferReceived);
|
||||
|
||||
public static HRESULT SendBuffer(ContextID sourceContext, ContextID destContext, RENDERHANDLE bufferHandle, BufferFlags flags, ReadOnlySpan<byte> data)
|
||||
{
|
||||
if (!ContextRegistry.TryGet(destContext, out BufferReceivedHandler handler))
|
||||
return HRESULT.E_FAIL;
|
||||
|
||||
handler(sourceContext, bufferHandle, flags, data);
|
||||
return HRESULT.S_OK;
|
||||
}
|
||||
|
||||
// ---- message pump ----------------------------------------------------------------
|
||||
|
||||
// Drains the render thread's message queue (running any deferred work posted to it) and
|
||||
// reports WorkResult flags: ProcessedMessage (1) if work ran, else None (0). Never
|
||||
// NewUserMessage (2) -- that flag drives Win32 TranslateMessage/DispatchMessage, which
|
||||
// needs a real HWND this backend-agnostic pump doesn't have; leaving it unset routes
|
||||
// ProcessNativeEvents down its non-Win32 branch. See logs/UIXrender/Rendering.md.
|
||||
public static uint PeekMessage(uint filterMin, uint filterMax, uint removeMsg) =>
|
||||
MessagePump.Peek() ? 1u : 0u;
|
||||
|
||||
// Blocks the render thread until work is posted (e.g. InterThreadWake) or the timeout
|
||||
// elapses -- a real wait that returns early on a wake, not a fixed sleep.
|
||||
public static void WaitMessage(uint timeoutMs) => MessagePump.Wait(timeoutMs);
|
||||
|
||||
// A null function pointer is InterThreadWake: post a wake so a blocked WaitMessage
|
||||
// returns. A real pointer is a deferred invoke -- run inline when synchronous, else
|
||||
// queue it to run on the pump (render) thread the next time it peeks, rather than on a
|
||||
// random threadpool thread.
|
||||
public static HRESULT Invoke(ContextID context, IntPtr pfnInvoke, IntPtr pvArgs, bool synchronous)
|
||||
{
|
||||
if (pfnInvoke == IntPtr.Zero)
|
||||
{
|
||||
MessagePump.PostWake();
|
||||
return HRESULT.S_OK;
|
||||
}
|
||||
|
||||
if (synchronous)
|
||||
{
|
||||
RunInvoke(pfnInvoke, pvArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
IntPtr fn = pfnInvoke;
|
||||
IntPtr args = pvArgs;
|
||||
MessagePump.Post(() => RunInvoke(fn, args));
|
||||
}
|
||||
return HRESULT.S_OK;
|
||||
}
|
||||
|
||||
private static unsafe void RunInvoke(IntPtr fn, IntPtr args) =>
|
||||
((delegate* unmanaged<IntPtr, void>)fn)(args);
|
||||
|
||||
// Registers a windowing backend that feeds OS window/input messages into the pump.
|
||||
// UIXrender core never creates a window itself (that would require a concrete GLFW/SDL
|
||||
// backend, breaking backend-agnosticism); a host or optional backend package supplies
|
||||
// one through this seam. See logs/UIXrender/Rendering.md.
|
||||
public static void SetWindowMessageSource(IWindowMessageSource source) =>
|
||||
MessagePump.SetWindowSource(source);
|
||||
|
||||
// ---- remote channel (opaque IntPtr handles, never dereferenced) -------------------
|
||||
|
||||
public static HRESULT RemoteCreateServerStreams(string session, TransportProtocol protocol, out IntPtr sendStream, out IntPtr receiveStream)
|
||||
=> RemoteServerConnection.CreateServerStreams(protocol, session, out sendStream, out receiveStream);
|
||||
|
||||
public static HRESULT RemoteWaitServerStreamsConnected(TransportProtocol protocol, IntPtr sendStream)
|
||||
=> RemoteServerConnection.WaitConnected(protocol, sendStream);
|
||||
|
||||
public static HRESULT RemoteServerInit(IntPtr sendStream, ContextID context, BufferReceivedHandler onRemoteToLocal, out IntPtr session)
|
||||
=> RemoteServerConnection.ServerInit(sendStream, context, onRemoteToLocal, out session);
|
||||
|
||||
public static HRESULT RemoteServerUninit(IntPtr session, bool forceShutdown, out ShutdownReason reason)
|
||||
=> RemoteServerConnection.ServerUninit(session, forceShutdown, out reason);
|
||||
|
||||
// Backs SpObjectRelease in the managed-direct path, whose only callers release the
|
||||
// remote stream handles minted by RemoteCreateServerStreams.
|
||||
public static void ReleaseRemoteStream(IntPtr streamHandle)
|
||||
=> RemoteServerConnection.ReleaseHandle(streamHandle);
|
||||
|
||||
// ---- effects ---------------------------------------------------------------------
|
||||
|
||||
// HLSL effect compilation is a graphics-API-specific service with no cross-platform
|
||||
// abstraction available under this project's dependency policy -- so this reports
|
||||
// "not implemented" honestly rather than faking a compiled blob. See
|
||||
// logs/UIXrender/FullSurface.md, decision 1.
|
||||
public static HRESULT Dx9CompileEffect() => HRESULT.E_NOTIMPL;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Iris.Render.Engine;
|
||||
|
||||
// Every UIXrender export that hands an opaque "pointer" back to a caller (schema objects,
|
||||
// XML readers, rich-text objects, bitmaps, data queries, ...) uses this: the managed
|
||||
// object is pinned by a GCHandle and the caller only ever sees the GCHandle's IntPtr.
|
||||
// This is the same convention SpRenderThreadInit already established for its thread
|
||||
// handle -- centralised here so ~10 subsystems don't each re-derive it.
|
||||
//
|
||||
// Deliberately not a "real" pointer: the original native library handed out genuine C++
|
||||
// object pointers, but nothing outside UIXrender is allowed to dereference these (the
|
||||
// managed declarations all type them as opaque IntPtr/HANDLE), so an indirection is
|
||||
// both safe and much harder to corrupt.
|
||||
internal static class HandleTable
|
||||
{
|
||||
public static IntPtr Alloc(object value) =>
|
||||
value == null ? IntPtr.Zero : GCHandle.ToIntPtr(GCHandle.Alloc(value, GCHandleType.Normal));
|
||||
|
||||
public static T Get<T>(IntPtr handle) where T : class =>
|
||||
handle == IntPtr.Zero ? null : GCHandle.FromIntPtr(handle).Target as T;
|
||||
|
||||
public static bool TryGet<T>(IntPtr handle, out T value) where T : class
|
||||
{
|
||||
value = Get<T>(handle);
|
||||
return value != null;
|
||||
}
|
||||
|
||||
public static void Free(IntPtr handle)
|
||||
{
|
||||
if (handle == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
GCHandle gc = GCHandle.FromIntPtr(handle);
|
||||
if (gc.IsAllocated)
|
||||
{
|
||||
(gc.Target as IDisposable)?.Dispose();
|
||||
gc.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.Iris.Render.Interop;
|
||||
|
||||
namespace Microsoft.Iris.Render.Engine;
|
||||
|
||||
public interface IRenderThreadHandle : IDisposable
|
||||
{
|
||||
ContextID ContextId { get; }
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.Iris.Render.Interop;
|
||||
|
||||
namespace Microsoft.Iris.Render.Engine;
|
||||
|
||||
// Backs EngineService.StartRenderThread: spins up a genuine OS thread the CLR didn't
|
||||
// create, which invokes the registered handler once (with synthetic data, proving the
|
||||
// round trip) and then waits for shutdown -- this is the exact mechanism every
|
||||
// Remote*/Local*Callback class in UIX.RenderApi depends on via LocalChannel.Connect().
|
||||
// See logs/UIXrender/EngineCore.md. Not yet the full message-pump loop (that's the
|
||||
// larger "Engine core" subsystem, still ahead) -- this proves the interop mechanism,
|
||||
// not the whole dispatch engine. No pointers here at all now -- callers (native or
|
||||
// managed) each supply an ordinary BufferReceivedHandler delegate.
|
||||
internal sealed class RenderThread : IRenderThreadHandle
|
||||
{
|
||||
private readonly BufferReceivedHandler _handler;
|
||||
private readonly ManualResetEventSlim _shutdown = new(false);
|
||||
private readonly Thread _thread;
|
||||
|
||||
public ContextID ContextId { get; }
|
||||
|
||||
private RenderThread(ContextID contextId, BufferReceivedHandler handler)
|
||||
{
|
||||
ContextId = contextId;
|
||||
_handler = handler;
|
||||
_thread = new Thread(Run) { IsBackground = true, Name = $"UIXrender.RenderThread[{contextId.value}]" };
|
||||
}
|
||||
|
||||
public static RenderThread Start(ContextID contextId, BufferReceivedHandler handler)
|
||||
{
|
||||
var thread = new RenderThread(contextId, handler);
|
||||
ContextRegistry.Register(contextId, handler);
|
||||
thread._thread.Start();
|
||||
return thread;
|
||||
}
|
||||
|
||||
private void Run()
|
||||
{
|
||||
_handler?.Invoke(ContextId, RENDERHANDLE.NULL, default, ReadOnlySpan<byte>.Empty);
|
||||
_shutdown.Wait();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ContextRegistry.Unregister(ContextId);
|
||||
_shutdown.Set();
|
||||
_thread.Join();
|
||||
_shutdown.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Iris.Render.Interop;
|
||||
|
||||
// Bit-for-bit mirror of EngineApi.BufferFlags/EngineApi.BufferInfo
|
||||
// (UIX.RenderApi/Microsoft/Iris/Render/Protocol/EngineApi.cs).
|
||||
[Flags]
|
||||
public enum BufferFlags
|
||||
{
|
||||
IsBatch = 1,
|
||||
CopyData = 2,
|
||||
Valid = CopyData | IsBatch,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct BufferInfo
|
||||
{
|
||||
public ContextID idContextSrc;
|
||||
public ContextID idContextDest;
|
||||
public RENDERHANDLE idBuffer;
|
||||
public BufferFlags nFlags;
|
||||
public uint cbSizeBuffer;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Iris.Render.Interop.Com;
|
||||
|
||||
// Several UIXrender exports take a COM callback interface (IUIXListCallbacks,
|
||||
// IRawUIXServices, IRichTextCallbacks, IImeCallbacks). Their managed declarations use
|
||||
// [MarshalAs(UnmanagedType.Interface)], but an [UnmanagedCallersOnly] method cannot
|
||||
// accept a non-blittable parameter -- what actually arrives on the wire either way is a
|
||||
// pointer to the object's vtable-bearing COM instance. So UIXrender receives IntPtr and
|
||||
// calls through the vtable here.
|
||||
//
|
||||
// This is the "reconstruct the interface rather than invent a raw pointer type" approach
|
||||
// CLAUDE.md's *COM objects* section prescribes, applied in the callee direction: slot
|
||||
// numbering starts at 3 because every one of these interfaces is
|
||||
// ComInterfaceType.InterfaceIsIUnknown, so slots 0/1/2 are
|
||||
// QueryInterface/AddRef/Release, and the declared methods follow in declaration order.
|
||||
internal static unsafe class ComVtable
|
||||
{
|
||||
// Slot 0/1/2 of IUnknown.
|
||||
public const int SlotQueryInterface = 0;
|
||||
public const int SlotAddRef = 1;
|
||||
public const int SlotRelease = 2;
|
||||
|
||||
// First slot available to a derived interface's own methods.
|
||||
public const int FirstMethodSlot = 3;
|
||||
|
||||
public static void* Slot(IntPtr comObject, int slot)
|
||||
{
|
||||
if (comObject == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
void** vtable = *(void***)comObject;
|
||||
return vtable[slot];
|
||||
}
|
||||
|
||||
public static uint AddRef(IntPtr comObject)
|
||||
{
|
||||
void* fn = Slot(comObject, SlotAddRef);
|
||||
return fn == null ? 0 : ((delegate* unmanaged<IntPtr, uint>)fn)(comObject);
|
||||
}
|
||||
|
||||
public static uint Release(IntPtr comObject)
|
||||
{
|
||||
void* fn = Slot(comObject, SlotRelease);
|
||||
return fn == null ? 0 : ((delegate* unmanaged<IntPtr, uint>)fn)(comObject);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Iris.Render.Interop;
|
||||
|
||||
// Bit-for-bit mirror of Microsoft.Iris.Render.Protocol.ContextID
|
||||
// (UIX.RenderApi/Microsoft/Iris/Render/Protocol/ContextID.cs).
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ContextID
|
||||
{
|
||||
public static readonly ContextID NULL = new(0);
|
||||
public static readonly ContextID CURRENT = new(uint.MaxValue);
|
||||
|
||||
public uint value;
|
||||
|
||||
public ContextID(uint value) => this.value = value;
|
||||
|
||||
public static bool operator ==(ContextID a, ContextID b) => a.value == b.value;
|
||||
public static bool operator !=(ContextID a, ContextID b) => a.value != b.value;
|
||||
|
||||
public override bool Equals(object obj) => obj is ContextID other && value == other.value;
|
||||
public override int GetHashCode() => (int)value;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Iris.Render.Interop.Drawing;
|
||||
|
||||
// Bit-for-bit mirror of Microsoft.Iris.Drawing.Color (UIX/Microsoft/Iris/Drawing/Color.cs)
|
||||
// -- a single packed 0xAARRGGBB uint, not four separate byte fields.
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Color
|
||||
{
|
||||
public uint value;
|
||||
|
||||
public Color(uint value) => this.value = value;
|
||||
|
||||
public byte A => (byte)(value >> 24);
|
||||
public byte R => (byte)(value >> 16);
|
||||
public byte G => (byte)(value >> 8);
|
||||
public byte B => (byte)value;
|
||||
|
||||
public static Color FromArgb(byte a, byte r, byte g, byte b) =>
|
||||
new((uint)((a << 24) | (r << 16) | (g << 8) | b));
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Iris.Render.Interop.Drawing;
|
||||
|
||||
// Bit-for-bit mirror of Microsoft.Iris.Render.ColorF (UIX.RenderApi/Microsoft/Iris/Render/ColorF.cs).
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ColorF
|
||||
{
|
||||
public float a;
|
||||
public float r;
|
||||
public float g;
|
||||
public float b;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Iris.Render.Interop.Drawing;
|
||||
|
||||
// Bit-for-bit mirror of Microsoft.Iris.Render.Point (UIX.RenderApi/Microsoft/Iris/Render/Point.cs).
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Point
|
||||
{
|
||||
public int x;
|
||||
public int y;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Iris.Render.Interop.Drawing;
|
||||
|
||||
// Bit-for-bit mirror of Microsoft.Iris.Render.Rectangle (UIX.RenderApi/Microsoft/Iris/Render/Rectangle.cs).
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Rectangle
|
||||
{
|
||||
public int x;
|
||||
public int y;
|
||||
public int width;
|
||||
public int height;
|
||||
}
|
||||
|
||||
// Bit-for-bit mirror of Microsoft.Iris.RenderAPI.Drawing.RectangleF
|
||||
// (UIX/Microsoft/Iris/RenderAPI/Drawing/RectangleF.cs).
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RectangleF
|
||||
{
|
||||
public float x;
|
||||
public float y;
|
||||
public float width;
|
||||
public float height;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Iris.Render.Interop.Drawing;
|
||||
|
||||
// Bit-for-bit mirror of Microsoft.Iris.Render.Size (UIX.RenderApi/Microsoft/Iris/Render/Size.cs).
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Size
|
||||
{
|
||||
public int width;
|
||||
public int height;
|
||||
|
||||
public Size(int width, int height)
|
||||
{
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Iris.Render.Interop.Drawing;
|
||||
|
||||
// Bit-for-bit mirror of Microsoft.Iris.RenderAPI.Drawing.SizeF (UIX/Microsoft/Iris/RenderAPI/Drawing/SizeF.cs).
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SizeF
|
||||
{
|
||||
public float width;
|
||||
public float height;
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
#if NETCOREAPP
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Iris.Interop;
|
||||
using Microsoft.Iris.Render.Engine;
|
||||
using Microsoft.Iris.Render.Interop.Protocol;
|
||||
using Microsoft.Iris.Render.Interop.Win32;
|
||||
|
||||
namespace Microsoft.Iris.Render.Interop;
|
||||
|
||||
// [UnmanagedCallersOnly] exports matching the core transport DllImport("UIXRender.dll")
|
||||
// declarations in UIX.RenderApi/Microsoft/Iris/Render/Protocol/EngineApi.cs. This is
|
||||
// the *only* place in UIXrender that deals with raw pointers/unmanaged function
|
||||
// pointers -- everything it calls into (Microsoft.Iris.Render.Engine.EngineService) is
|
||||
// ordinary, pointer-free managed C#. See logs/UIXrender/EngineCore.md for why
|
||||
// SpInit/SpUninit are stubs and SpWrapBufferProc is a pass-through rather than a real
|
||||
// wrapper, and logs/UIXrender/FullSurface.md for the rest of this file (added later).
|
||||
public static unsafe class EngineApi
|
||||
{
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpInit")]
|
||||
public static HRESULT SpInit(InitArgs* args) => HRESULT.S_OK;
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpUninit")]
|
||||
public static HRESULT SpUninit() => HRESULT.S_OK;
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpWrapBufferProc")]
|
||||
public static HRESULT SpWrapBufferProc(IntPtr pfnProcessBufferProc, IntPtr* ppNativeProc)
|
||||
{
|
||||
if (ppNativeProc == null)
|
||||
return HRESULT.E_INVALIDARG;
|
||||
|
||||
*ppNativeProc = pfnProcessBufferProc;
|
||||
return HRESULT.S_OK;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpRenderThreadInit")]
|
||||
public static HRESULT SpRenderThreadInit(InitArgs* argsRender, IntPtr* pThread)
|
||||
{
|
||||
if (argsRender == null || pThread == null)
|
||||
return HRESULT.E_INVALIDARG;
|
||||
|
||||
ContextID contextId = argsRender->idContext;
|
||||
IntPtr nativeCallback = argsRender->pfnProcessBuffer;
|
||||
IntPtr callbackData = argsRender->pvProcessData;
|
||||
|
||||
// Adapts the raw native function pointer into the idiomatic BufferReceivedHandler
|
||||
// shape EngineService deals in -- all pointer reconstruction for this direction
|
||||
// lives here, not in Engine/.
|
||||
BufferReceivedHandler handler = (source, bufferHandle, flags, data) =>
|
||||
{
|
||||
if (nativeCallback == IntPtr.Zero)
|
||||
return;
|
||||
fixed (byte* pData = data)
|
||||
{
|
||||
var info = new BufferInfo
|
||||
{
|
||||
idContextSrc = source,
|
||||
idContextDest = contextId,
|
||||
idBuffer = bufferHandle,
|
||||
nFlags = flags,
|
||||
cbSizeBuffer = (uint)data.Length,
|
||||
};
|
||||
var fn = (delegate* unmanaged<IntPtr, uint, BufferInfo*, void*, int>)nativeCallback;
|
||||
fn(callbackData, contextId.value, &info, pData);
|
||||
}
|
||||
};
|
||||
|
||||
IRenderThreadHandle thread = EngineService.StartRenderThread(contextId, handler);
|
||||
*pThread = GCHandle.ToIntPtr(GCHandle.Alloc(thread, GCHandleType.Normal));
|
||||
return HRESULT.S_OK;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpRenderThreadUninit")]
|
||||
public static HRESULT SpRenderThreadUninit(IntPtr pThread)
|
||||
{
|
||||
if (pThread == IntPtr.Zero)
|
||||
return HRESULT.E_INVALIDARG;
|
||||
|
||||
GCHandle handle = GCHandle.FromIntPtr(pThread);
|
||||
(handle.Target as IDisposable)?.Dispose();
|
||||
handle.Free();
|
||||
return HRESULT.S_OK;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpBufferOpen")]
|
||||
public static HRESULT SpBufferOpen(BufferInfo* phdrData, void* pvData)
|
||||
{
|
||||
if (phdrData == null)
|
||||
return HRESULT.E_INVALIDARG;
|
||||
|
||||
var span = new ReadOnlySpan<byte>(pvData, (int)phdrData->cbSizeBuffer);
|
||||
return EngineService.SendBuffer(phdrData->idContextSrc, phdrData->idContextDest, phdrData->idBuffer, phdrData->nFlags, span);
|
||||
}
|
||||
|
||||
// Real, but a documented no-op: this reimplementation has no Win32 message queue to
|
||||
// pump (no window is created anywhere in this repo yet), and LocalChannel.Connect()
|
||||
// -- the only path real Zune uses -- never calls this (see logs/UIXrender/EngineCore.md's
|
||||
// SpInit/SpUninit note for the same "IGMM_STANDARD messaging model" open question).
|
||||
// Always reports "no message available" rather than guessing at pump semantics.
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpPeekMessage")]
|
||||
public static HRESULT SpPeekMessage(MSG* msg, HWND hwnd, uint nMsgFilterMin, uint nMsgFilterMax, uint wRemoveMsg, uint* nResult)
|
||||
{
|
||||
if (msg == null || nResult == null)
|
||||
return HRESULT.E_INVALIDARG;
|
||||
*msg = default;
|
||||
*nResult = EngineService.PeekMessage(nMsgFilterMin, nMsgFilterMax, wRemoveMsg);
|
||||
return HRESULT.S_OK;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpWaitMessage")]
|
||||
public static HRESULT SpWaitMessage(uint nTimeOutMs, IntPtr unused)
|
||||
{
|
||||
EngineService.WaitMessage(nTimeOutMs);
|
||||
return HRESULT.S_OK;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpInvoke")]
|
||||
public static HRESULT SpInvoke(ContextID idContext, IntPtr pfnInvoke, IntPtr pvArgs, int synchronous) =>
|
||||
EngineService.Invoke(idContext, pfnInvoke, pvArgs, synchronous != 0);
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpRemoteCreateServerStreams")]
|
||||
public static HRESULT SpRemoteCreateServerStreams(char* stSession, TransportProtocol nProtocol, IntPtr* pSendStream, IntPtr* pReceiveStream)
|
||||
{
|
||||
if (pSendStream == null || pReceiveStream == null)
|
||||
return HRESULT.E_INVALIDARG;
|
||||
|
||||
HRESULT hr = EngineService.RemoteCreateServerStreams(NativeString.UniToString(stSession), nProtocol, out IntPtr send, out IntPtr receive);
|
||||
*pSendStream = send;
|
||||
*pReceiveStream = receive;
|
||||
return hr;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpRemoteWaitServerStreamsConnected")]
|
||||
public static HRESULT SpRemoteWaitServerStreamsConnected(TransportProtocol nProtocol, IntPtr pSendStream, IntPtr pReceiveStream) =>
|
||||
EngineService.RemoteWaitServerStreamsConnected(nProtocol, pSendStream);
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpRemoteServerInit")]
|
||||
public static HRESULT SpRemoteServerInit(IntPtr pSendStream, IntPtr pReceiveStream, InitArgs argsSend, IntPtr* pSession)
|
||||
{
|
||||
if (pSession == null)
|
||||
return HRESULT.E_INVALIDARG;
|
||||
|
||||
// Adapt the raw native process-buffer function pointer into a BufferReceivedHandler,
|
||||
// exactly as SpRenderThreadInit does -- RemoteServerConnection deals only in the
|
||||
// pointer-free handler shape.
|
||||
ContextID contextId = argsSend.idContext;
|
||||
IntPtr nativeCallback = argsSend.pfnProcessBuffer;
|
||||
IntPtr callbackData = argsSend.pvProcessData;
|
||||
BufferReceivedHandler handler = nativeCallback == IntPtr.Zero
|
||||
? null
|
||||
: (source, bufferHandle, flags, data) =>
|
||||
{
|
||||
fixed (byte* pData = data)
|
||||
{
|
||||
var info = new BufferInfo
|
||||
{
|
||||
idContextSrc = source,
|
||||
idContextDest = contextId,
|
||||
idBuffer = bufferHandle,
|
||||
nFlags = flags,
|
||||
cbSizeBuffer = (uint)data.Length,
|
||||
};
|
||||
var fn = (delegate* unmanaged<IntPtr, uint, BufferInfo*, void*, int>)nativeCallback;
|
||||
fn(callbackData, source.value, &info, pData);
|
||||
}
|
||||
};
|
||||
|
||||
HRESULT hr = EngineService.RemoteServerInit(pSendStream, contextId, handler, out IntPtr session);
|
||||
*pSession = session;
|
||||
return hr;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpRemoteServerUninit")]
|
||||
public static HRESULT SpRemoteServerUninit(IntPtr pSession, int fForceShutdown, ShutdownReason* nShutdownReason)
|
||||
{
|
||||
if (nShutdownReason == null)
|
||||
return HRESULT.E_INVALIDARG;
|
||||
|
||||
HRESULT hr = EngineService.RemoteServerUninit(pSession, fForceShutdown != 0, out ShutdownReason reason);
|
||||
*nShutdownReason = reason;
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Deliberately not implemented: HLSL effect compilation exists only as a
|
||||
// graphics-API-specific service (d3dcompiler_47.dll / Silk.NET.Direct3D.Compilers),
|
||||
// and Silk.NET has no cross-platform abstraction over it -- so per this project's
|
||||
// "abstractions, not specific graphics APIs" dependency policy there is nothing
|
||||
// correct to call here yet. Reports failure honestly (with empty out-params, so a
|
||||
// caller that ignores the HRESULT still sees a well-defined "no blob") rather than
|
||||
// pretending success. See logs/UIXrender/FullSurface.md, decision 1.
|
||||
// TODO: revisit once this project has a rendering backend of its own to compile for.
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpDx9CompileEffect")]
|
||||
public static HRESULT SpDx9CompileEffect(byte* stEffect, byte* stDefines, IntPtr* pErrorString, IntPtr* pErrorBuffer, IntPtr* pEffectBlob, uint* effectBlobSize, IntPtr* pEffectBlobBuffer)
|
||||
{
|
||||
// Empty out-params so a caller ignoring the HRESULT still sees a well-defined
|
||||
// "no blob"; the not-implemented decision itself lives in EngineService.
|
||||
if (pErrorString != null) *pErrorString = IntPtr.Zero;
|
||||
if (pErrorBuffer != null) *pErrorBuffer = IntPtr.Zero;
|
||||
if (pEffectBlob != null) *pEffectBlob = IntPtr.Zero;
|
||||
if (effectBlobSize != null) *effectBlobSize = 0;
|
||||
if (pEffectBlobBuffer != null) *pEffectBlobBuffer = IntPtr.Zero;
|
||||
return EngineService.Dx9CompileEffect();
|
||||
}
|
||||
|
||||
// Generic COM release: calls IUnknown::Release through the object's own vtable
|
||||
// (slot 2, after QueryInterface/AddRef) rather than assuming anything about what
|
||||
// concrete type pUnknown is -- this export exists precisely to release arbitrary
|
||||
// IUnknown-shaped pointers handed across the boundary.
|
||||
[UnmanagedCallersOnly(EntryPoint = "SpObjectRelease")]
|
||||
public static void SpObjectRelease(IntPtr pUnknown)
|
||||
{
|
||||
if (pUnknown == IntPtr.Zero)
|
||||
return;
|
||||
void* vtbl = *(void**)pUnknown;
|
||||
var release = (delegate* unmanaged<IntPtr, uint>)(*((void**)vtbl + 2));
|
||||
release(pUnknown);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,84 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Iris.Render.Interop.Drawing;
|
||||
|
||||
namespace Microsoft.Iris.Render.Interop.Extensions;
|
||||
|
||||
// Bit-for-bit mirrors of the ExtensionsApi.cs (UIX.RenderApi/Microsoft/Iris/Render/Extensions/*.cs)
|
||||
// asset-loading types: ImageRequirements, ImageHeader, ImageData, ImageInformation,
|
||||
// HSpBitmap, SurfaceFormat.
|
||||
|
||||
public enum SurfaceFormat : uint
|
||||
{
|
||||
None = 0,
|
||||
Bpp8 = 0x00080000,
|
||||
A8 = 0x00088000,
|
||||
Bpp16 = 0x00100000,
|
||||
RGB16_555 = 0x00100555,
|
||||
RGB16_565 = 0x00100565,
|
||||
ARGB16_1555 = 0x00101555,
|
||||
Bpp24 = 0x00180000,
|
||||
RGB24 = 0x00180888,
|
||||
Bpp32 = 0x00200000,
|
||||
RGB32 = 0x00200888,
|
||||
ARGB32 = 0x00208888,
|
||||
YUY2 = 0x21100000,
|
||||
External = 0x80000000,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ImageRequirementsFields
|
||||
{
|
||||
None = 0,
|
||||
MaximumSize = 1,
|
||||
Border = 2,
|
||||
Flippable = 4,
|
||||
AntialiasEdges = 16,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ImageRequirements
|
||||
{
|
||||
public ImageRequirementsFields mask;
|
||||
public Size maximumSizePxl;
|
||||
public int borderPxl;
|
||||
public ColorF borderColor;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ImageHeader
|
||||
{
|
||||
public Size sizeActualPxl;
|
||||
public Size sizeOriginalPxl;
|
||||
public int stride;
|
||||
public SurfaceFormat format;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ImageData
|
||||
{
|
||||
public IntPtr rgData;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ImageInformation
|
||||
{
|
||||
public ImageHeader header;
|
||||
public ImageData data;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum BitmapOptions
|
||||
{
|
||||
None = 0,
|
||||
Decode = 1,
|
||||
Flip = 2,
|
||||
Valid = Flip | Decode,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct HSpBitmap
|
||||
{
|
||||
public IntPtr h;
|
||||
public static readonly HSpBitmap NULL = new();
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Iris.Render.Interop.Extensions;
|
||||
|
||||
// Bit-for-bit mirrors of ExtensionsApi.cs's sound types.
|
||||
[Flags]
|
||||
public enum SoundOptions
|
||||
{
|
||||
None = 0,
|
||||
Decode = 1,
|
||||
BigEndian = 2,
|
||||
Valid = BigEndian | Decode,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SoundHeader
|
||||
{
|
||||
public ushort formatTag;
|
||||
public ushort channels;
|
||||
public uint samplesPerSec;
|
||||
public uint avgBytesPerSec;
|
||||
public ushort blockAlign;
|
||||
public ushort bitsPerSample;
|
||||
public ushort cbExtraData;
|
||||
public uint cbDataSize;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SoundData
|
||||
{
|
||||
public IntPtr rgData;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SoundInformation
|
||||
{
|
||||
public SoundHeader header;
|
||||
public SoundData data;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct HSpSound
|
||||
{
|
||||
public IntPtr h;
|
||||
public static readonly HSpSound NULL = new();
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Iris.Render.Interop;
|
||||
|
||||
// Bit-for-bit mirror of the decompiled Microsoft.Iris.Render.Internal.HRESULT
|
||||
// (UIX.RenderApi/Microsoft/Iris/Render/Internal/HRESULT.cs) -- a single int, so every
|
||||
// [UnmanagedCallersOnly] method returning HRESULT here matches what EngineApi.cs's
|
||||
// [DllImport] declarations expect byte-for-byte. The S_OK/E_* constants aren't part of
|
||||
// the original decompiled surface (nothing to break by adding them -- this is new code
|
||||
// with no existing dependents), added here purely as return-value conveniences.
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct HRESULT
|
||||
{
|
||||
public static readonly HRESULT S_OK = new(0);
|
||||
public static readonly HRESULT E_FAIL = new(unchecked((int)0x80004005));
|
||||
public static readonly HRESULT E_INVALIDARG = new(unchecked((int)0x80070057));
|
||||
public static readonly HRESULT E_NOTIMPL = new(unchecked((int)0x80004001));
|
||||
|
||||
public int hr;
|
||||
|
||||
public HRESULT(int hr) => this.hr = hr;
|
||||
|
||||
public static bool operator ==(HRESULT a, HRESULT b) => a.hr == b.hr;
|
||||
public static bool operator !=(HRESULT a, HRESULT b) => a.hr != b.hr;
|
||||
|
||||
public override bool Equals(object obj) => obj is HRESULT other && hr == other.hr;
|
||||
public override int GetHashCode() => hr;
|
||||
|
||||
public bool IsError() => hr < 0;
|
||||
public bool IsSuccess() => hr >= 0;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Iris.Render.Interop;
|
||||
|
||||
// Bit-for-bit mirror of EngineApi.InitArgs
|
||||
// (UIX.RenderApi/Microsoft/Iris/Render/Protocol/EngineApi.cs). The managed side declares
|
||||
// pfnTimeout as a delegate (TimeoutEventHandler); the CLR marshals a delegate field to a
|
||||
// plain function pointer before the struct crosses into native code, so it's IntPtr here
|
||||
// -- same wire bytes, no delegate type on our side.
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct InitArgs
|
||||
{
|
||||
public uint cbSize;
|
||||
public ContextID idContext;
|
||||
public int cItemsPerGroupBits;
|
||||
public int cGroupBits;
|
||||
public IntPtr pfnProcessBuffer;
|
||||
public IntPtr pvProcessData;
|
||||
public RENDERHANDLE idObjectBrokerClass;
|
||||
public IntPtr pfnTimeout;
|
||||
public IntPtr pvTimeoutData;
|
||||
public uint nTimeOutSec;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user