Empty UIXrender and UIXsup, retry impl

This commit is contained in:
Joshua "Yoshi" Askharoun
2026-07-25 14:02:57 -05:00
parent b0bc4e5dba
commit 651dcaba0b
67 changed files with 0 additions and 6150 deletions
@@ -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);
-21
View File
@@ -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);
}
-108
View File
@@ -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;
}
-42
View File
@@ -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();
}
}
}
-9
View File
@@ -1,9 +0,0 @@
using System;
using Microsoft.Iris.Render.Interop;
namespace Microsoft.Iris.Render.Engine;
public interface IRenderThreadHandle : IDisposable
{
ContextID ContextId { get; }
}
-51
View File
@@ -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();
}
}
-24
View File
@@ -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;
}
-47
View File
@@ -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);
}
}
-22
View File
@@ -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;
}
-21
View File
@@ -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));
}
-13
View File
@@ -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;
}
-11
View File
@@ -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;
}
-24
View File
@@ -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;
}
-17
View File
@@ -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;
}
}
-11
View File
@@ -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;
}
-221
View File
@@ -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();
}
-31
View File
@@ -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;
}
-24
View File
@@ -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