Inhaltsverzeichnis

  1. Einleitung
  2. SlimDX
  3. GraphicsDevice
  4. RenderTarget
  5. Freigabe
  6. Anwendungsbeispiele

Kommentare

Interoperation zwischen XNA und SlimDX

Autor: SteveKr

^ Einleitung


Bekannterweise versteckt das XNA-Framework interne Zeiger auf DirectX-Objekte, wie Devices, Surfaces, etc. Ohne diese bleiben XNA-Projekten viele Anwendungsbereiche verschlossen.
In diesem Artikel wird diese Einschränkung mit Hilfe von Reflection und SlimDX aufgehoben und der Zugriff auf diese Objekte per Extension Methods realisiert.

^ SlimDX


Das SDK für SlimDX kann hier heruntergeladen werden:
:arrow: http://code.google.com/p/slimdx/wiki/Downloads?tm=2

^ GraphicsDevice


Die GraphicsDevice-Klasse besitzt einen internen Zeiger pComPtr, der auf das DirectX-Device-Objekt verweist. Den Wert des Feldes können wir mittels Reflection abfragen:
public unsafe static IntPtr GetPointer(this GraphicsDevice instance)
{
    FieldInfo comPtr = typeof(GraphicsDevice).GetField(
        "pComPtr",
        BindingFlags.NonPublic | BindingFlags.Instance
        );

    return new IntPtr(Pointer.Unbox(comPtr.GetValue(instance)))
}


Die folgende Methode nutzt die obige und erstellt ein SlimDX-Device-Objekt aus dem Zeiger der XNA-GraphicsDevice.
public static Device ToDevice(this GraphicsDevice instance)
{
    return Device.FromPointer(instance.GetPointer());
}


Diese Methode liefert ein SlimDX-Surface-Objekt des Backbuffers.
public static Surface GetBackBuffer(this GraphicsDevice instance)
{
    return instance.ToDevice().GetBackBuffer(0, 0);
}


Lockable Backbuffer


Eine weitere Beschränkung bei XNA ist, das der Backbuffer nicht Lockable ist, was aber in einigen Fällen wünschenswert bzw. notwendig ist. Normalerweise könnte man diese Eigenschaft über die Flags der PresentParameters einstellen (C++-Code):
PParams.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;

Wenn wir uns das Äquivalent in XNA anschauen merken wir allerdings, dass in der Enumeration PresentOptions kein passendes Element vorhanden ist.
pp.PresentOptions = PresentOptions.?

Aus welchen Gründen auch immer die Funktion nicht implementiert wurde, man kann diese Beschränkung einfach umgehen indem man den Wert 1, der für den "Lockable Backbuffer" steht, zuweist:
pp.PresentOptions = (PresentOptions)1;


Die folgende Methode macht aus einem bestehenden GraphicsDevice einen mit lockable Backbuffer.
public static void SetLockable(this GraphicsDevice instance)
{
    PresentationParameters pp = instance.PresentationParameters;
    pp.PresentOptions |= (PresentOptions)1;

    instance.Reset(pp);
}


^ RenderTarget


Bei RenderTargets verhält es sich ähnlich wie beim GraphicsDevice. Auch hier steckt der Zeiger auf das DirectX-Objekt im internen Feld pComPtr:
public unsafe static IntPtr GetPointer(this RenderTarget2D instance)
{
    FieldInfo comPtr = typeof(RenderTarget2D).GetField(
        "pComPtr",
        BindingFlags.NonPublic | BindingFlags.Instance
        );

    return new IntPtr(Pointer.Unbox(comPtr.GetValue(instance)));
}


Und die Methode um aus einem XNA-RenderTarget ein SlimDX-Surface zu erstellen.
public static Surface ToSurface(this RenderTarget2D instance)
{
    return Surface.FromPointer(instance.GetPointer());
}


^ Freigabe


Zu beachten ist, dass jedes SlimDX-Objekt, auch die, durch die hier vorgestellten Methoden erstellten, beim Beenden der Anwendung manuell mit der Dispose-Methode freigegeben werden muss.
Am Einfachsten gelingt das, indem man die Liste im ObjectTable-Objekt durchläuft, die alle SlimDX-Objekte enthält.
foreach (ComObject com in ObjectTable.Objects)
{
    com.Dispose();
}


^ Anwendungsbeispiele


Tutorials, die auf die hier beschriebenen Methoden zurückgreifen:
  • Transparente DirectX-Fenster (coming soon)
  • Vektorgrafiken mit Mono.Cairo zeichnen (coming soon)