Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Sie können Direct3D9-Inhalte in eine WPF-Anwendung (Windows Presentation Foundation) einschließen. In diesem Thema wird beschrieben, wie Sie Direct3D9-Inhalte erstellen, damit sie effizient mit WPF interagiert.
Hinweis
Wenn Sie Direct3D9-Inhalte in WPF verwenden, müssen Sie auch die Leistung berücksichtigen. Weitere Informationen zum Optimieren der Leistung finden Sie unter Leistungsüberlegungen für Direct3D9 und WPF-Interoperabilität.
Puffer anzeigen
Die D3DImage Klasse verwaltet zwei Anzeigepuffer, die als Hintergrundpuffer und Frontpuffer bezeichnet werden. Der Hintergrundpuffer ist Ihre Direct3D9-Oberfläche. Änderungen am Hintergrundpuffer werden beim Aufrufen der Unlock Methode vorwärts an den Frontpuffer kopiert.
Die folgende Abbildung zeigt die Beziehung zwischen dem Hintergrundpuffer und dem Frontpuffer.
Erstellen von Direct3D9-Geräten
Zum Rendern von Direct3D9-Inhalten müssen Sie ein Direct3D9-Gerät erstellen. Es gibt zwei Direct3D9-Objekte, mit denen Sie ein Gerät erstellen können, IDirect3D9 und IDirect3D9Ex. Verwenden Sie diese Objekte, um IDirect3DDevice9- und IDirect3DDevice9Ex-Geräte zu erstellen.
Erstellen Sie ein Gerät, indem Sie eine der folgenden Methoden aufrufen.
IDirect3D9 * Direct3DCreate9(UINT SDKVersion);HRESULT Direct3DCreate9Ex(UINT SDKVersion, IDirect3D9Ex **ppD3D);
Verwenden Sie unter Windows Vista oder höher die Direct3DCreate9Ex Methode mit einer Anzeige, die für die Verwendung des Windows Display Driver Model (WDDM) konfiguriert ist. Verwenden Sie die Direct3DCreate9 Methode auf jeder anderen Plattform.
Verfügbarkeit der Direct3DCreate9Ex-Methode
Die d3d9.dll verfügt nur über die Direct3DCreate9Ex Methode unter Windows Vista oder höher. Wenn Sie die Funktion unter Windows XP direkt verknüpfen, kann die Anwendung nicht geladen werden. Um festzustellen, ob die Methode unterstützt wird, laden Sie die Direct3DCreate9Ex DLL, und suchen Sie nach der Proc-Adresse. Der folgende Code zeigt, wie Sie die Direct3DCreate9Ex Methode testen. Ein vollständiges Codebeispiel finden Sie unter Walkthrough: Creating Direct3D9 Content for Hosting in WPF.
HRESULT
CRendererManager::EnsureD3DObjects()
{
HRESULT hr = S_OK;
HMODULE hD3D = NULL;
if (!m_pD3D)
{
hD3D = LoadLibrary(TEXT("d3d9.dll"));
DIRECT3DCREATE9EXFUNCTION pfnCreate9Ex = (DIRECT3DCREATE9EXFUNCTION)GetProcAddress(hD3D, "Direct3DCreate9Ex");
if (pfnCreate9Ex)
{
IFC((*pfnCreate9Ex)(D3D_SDK_VERSION, &m_pD3DEx));
IFC(m_pD3DEx->QueryInterface(__uuidof(IDirect3D9), reinterpret_cast<void **>(&m_pD3D)));
}
else
{
m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
if (!m_pD3D)
{
IFC(E_FAIL);
}
}
m_cAdapters = m_pD3D->GetAdapterCount();
}
Cleanup:
if (hD3D)
{
FreeLibrary(hD3D);
}
return hr;
}
HWND-Erstellung
Zum Erstellen eines Geräts ist ein HWND erforderlich. Im Allgemeinen erstellen Sie einen Dummy HWND für Direct3D9, der verwendet werden soll. Das folgende Codebeispiel zeigt, wie Sie einen Dummy HWND erstellen.
HRESULT
CRendererManager::EnsureHWND()
{
HRESULT hr = S_OK;
if (!m_hwnd)
{
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = DefWindowProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = NULL;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
IFC(E_FAIL);
}
m_hwnd = CreateWindow(szAppName,
TEXT("D3DImageSample"),
WS_OVERLAPPEDWINDOW,
0, // Initial X
0, // Initial Y
0, // Width
0, // Height
NULL,
NULL,
NULL,
NULL);
}
Cleanup:
return hr;
}
Präsentparameter
Das Erstellen eines Geräts erfordert auch eine D3DPRESENT_PARAMETERS Struktur, aber nur einige Parameter sind wichtig. Diese Parameter werden ausgewählt, um den Speicherbedarf zu minimieren.
Legen Sie die BackBufferHeight Felder auf BackBufferWidth 1 fest. Wenn Sie sie auf 0 festlegen, werden sie auf die Abmessungen des HWND festgelegt.
Legen Sie immer die D3DCREATE_MULTITHREADED Kennzeichnungen D3DCREATE_FPU_PRESERVE fest, um zu verhindern, dass der von Direct3D9 verwendete Arbeitsspeicher beschädigt wird, und verhindern Sie, dass Direct3D9 FPU-Einstellungen ändert.
Der folgende Code zeigt, wie die D3DPRESENT_PARAMETERS Struktur initialisiert wird.
HRESULT
CRenderer::Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter)
{
HRESULT hr = S_OK;
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.BackBufferHeight = 1;
d3dpp.BackBufferWidth = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
D3DCAPS9 caps;
DWORD dwVertexProcessing;
IFC(pD3D->GetDeviceCaps(uAdapter, D3DDEVTYPE_HAL, &caps));
if ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == D3DDEVCAPS_HWTRANSFORMANDLIGHT)
{
dwVertexProcessing = D3DCREATE_HARDWARE_VERTEXPROCESSING;
}
else
{
dwVertexProcessing = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
}
if (pD3DEx)
{
IDirect3DDevice9Ex *pd3dDevice = NULL;
IFC(pD3DEx->CreateDeviceEx(
uAdapter,
D3DDEVTYPE_HAL,
hwnd,
dwVertexProcessing | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
&d3dpp,
NULL,
&m_pd3dDeviceEx
));
IFC(m_pd3dDeviceEx->QueryInterface(__uuidof(IDirect3DDevice9), reinterpret_cast<void**>(&m_pd3dDevice)));
}
else
{
assert(pD3D);
IFC(pD3D->CreateDevice(
uAdapter,
D3DDEVTYPE_HAL,
hwnd,
dwVertexProcessing | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
&d3dpp,
&m_pd3dDevice
));
}
Cleanup:
return hr;
}
Erstellen des Hintergrundpuffer-Renderziels
Zum Anzeigen von Direct3D9-Inhalten in einem D3DImage, erstellen Sie eine Direct3D9-Oberfläche und weisen sie durch Aufrufen der SetBackBuffer Methode zu.
Überprüfen der Adapterunterstützung
Stellen Sie vor dem Erstellen einer Oberfläche sicher, dass alle Adapter die benötigten Oberflächeneigenschaften unterstützen. Selbst wenn Sie nur einen Adapter rendern, wird das WPF-Fenster möglicherweise auf jedem Adapter im System angezeigt. Sie sollten immer Direct3D9-Code schreiben, der Konfigurationen mit mehreren Adaptern verarbeitet, und Sie sollten alle Adapter auf Unterstützung überprüfen, da WPF die Oberfläche möglicherweise zwischen den verfügbaren Adaptern verschieben kann.
Das folgende Codebeispiel zeigt, wie alle Adapter auf dem System auf direct3D9-Unterstützung überprüft werden.
HRESULT
CRendererManager::TestSurfaceSettings()
{
HRESULT hr = S_OK;
D3DFORMAT fmt = m_fUseAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8;
//
// We test all adapters because because we potentially use all adapters.
// But even if this sample only rendered to the default adapter, you
// should check all adapters because WPF may move your surface to
// another adapter for you!
//
for (UINT i = 0; i < m_cAdapters; ++i)
{
// Can we get HW rendering?
IFC(m_pD3D->CheckDeviceType(
i,
D3DDEVTYPE_HAL,
D3DFMT_X8R8G8B8,
fmt,
TRUE
));
// Is the format okay?
IFC(m_pD3D->CheckDeviceFormat(
i,
D3DDEVTYPE_HAL,
D3DFMT_X8R8G8B8,
D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC, // We'll use dynamic when on XP
D3DRTYPE_SURFACE,
fmt
));
// D3DImage only allows multisampling on 9Ex devices. If we can't
// multisample, overwrite the desired number of samples with 0.
if (m_pD3DEx && m_uNumSamples > 1)
{
assert(m_uNumSamples <= 16);
if (FAILED(m_pD3D->CheckDeviceMultiSampleType(
i,
D3DDEVTYPE_HAL,
fmt,
TRUE,
static_cast<D3DMULTISAMPLE_TYPE>(m_uNumSamples),
NULL
)))
{
m_uNumSamples = 0;
}
}
else
{
m_uNumSamples = 0;
}
}
Cleanup:
return hr;
}
Erstellen des Surface
Stellen Sie vor dem Erstellen einer Oberfläche sicher, dass die Gerätefunktionen eine gute Leistung auf dem Zielbetriebssystem unterstützen. Weitere Informationen finden Sie unter Leistungsüberlegungen für Direct3D9 und WPF-Interoperabilität.
Wenn Sie über überprüfte Gerätefunktionen verfügen, können Sie die Oberfläche erstellen. Das folgende Codebeispiel zeigt, wie das Renderziel erstellt wird.
HRESULT
CRenderer::CreateSurface(UINT uWidth, UINT uHeight, bool fUseAlpha, UINT m_uNumSamples)
{
HRESULT hr = S_OK;
SAFE_RELEASE(m_pd3dRTS);
IFC(m_pd3dDevice->CreateRenderTarget(
uWidth,
uHeight,
fUseAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8,
static_cast<D3DMULTISAMPLE_TYPE>(m_uNumSamples),
0,
m_pd3dDeviceEx ? FALSE : TRUE, // Lockable RT required for good XP perf
&m_pd3dRTS,
NULL
));
IFC(m_pd3dDevice->SetRenderTarget(0, m_pd3dRTS));
Cleanup:
return hr;
}
WDDM
Unter Windows Vista und späteren Betriebssystemen, die für die Verwendung des WDDM konfiguriert sind, können Sie eine Renderzieltextur erstellen und die Oberfläche der Ebene 0 an die SetBackBuffer Methode übergeben. Dieser Ansatz wird unter Windows XP nicht empfohlen, da Sie keine sperrbare Renderzieltextur erstellen können und die Leistung reduziert wird.
Verwalten des Gerätestatus
Die D3DImage Klasse verwaltet zwei Anzeigepuffer, die als Hintergrundpuffer und Frontpuffer bezeichnet werden. Der Hintergrundpuffer ist Ihre Direct3D-Oberfläche. Änderungen am Hintergrundpuffer werden beim Aufrufen der Unlock Methode, in der sie auf der Hardware angezeigt wird, vorwärts an den Frontpuffer kopiert. Gelegentlich ist der Frontpuffer nicht verfügbar. Dieser Mangel an Verfügbarkeit kann durch Bildschirmsperrung, exklusive Direct3D-Anwendungen, Benutzerwechsel oder andere Systemaktivitäten verursacht werden. In diesem Fall wird Ihre WPF-Anwendung durch die Behandlung des IsFrontBufferAvailableChanged Ereignisses benachrichtigt. Wie Ihre Anwendung reagiert, wenn der Frontpuffer nicht verfügbar wird, hängt davon ab, ob WPF so eingerichtet ist, dass es auf Softwarerendering zurückgreift. Die SetBackBuffer Methode verfügt über eine Überladung, die einen Parameter verwendet, der angibt, ob WPF auf das Softwarerendering zurückgreift.
Wenn Sie die SetBackBuffer(D3DResourceType, IntPtr) Überladung aufrufen oder die SetBackBuffer(D3DResourceType, IntPtr, Boolean) Überladung mit dem enableSoftwareFallback Parameter, der auf false festgelegt ist, aufrufen, gibt das Renderingsystem seinen Verweis auf den Backbuffer frei, wenn der Frontbuffer nicht verfügbar ist und nichts angezeigt wird. Wenn der Frontpuffer wieder verfügbar ist, löst das Renderingsystem das IsFrontBufferAvailableChanged Ereignis aus, um die WPF-Anwendung zu benachrichtigen. Sie können einen Ereignishandler für das IsFrontBufferAvailableChanged Ereignis erstellen, um das Rendering erneut mit einer gültigen Direct3D-Oberfläche neu zu starten. Um das Rendering neu zu starten, müssen Sie aufrufen SetBackBuffer.
Wenn Sie die SetBackBuffer(D3DResourceType, IntPtr, Boolean) Überladung mit dem enableSoftwareFallback Parametersatz trueaufrufen, behält das Renderingsystem seinen Verweis auf den Hintergrundpuffer bei, wenn der Frontpuffer nicht mehr verfügbar ist, sodass es nicht mehr erforderlich SetBackBuffer ist, wenn der Frontpuffer wieder verfügbar ist.
Wenn das Softwarerendering aktiviert ist, kann es Situationen geben, in denen das Gerät des Benutzers nicht verfügbar ist, aber das Renderingsystem behält einen Verweis auf die Direct3D-Oberfläche bei. Rufen Sie die TestCooperativeLevel Methode auf, um zu überprüfen, ob ein Direct3D9-Gerät nicht verfügbar ist. Um eine Direct3D9Ex-Geräte zu überprüfen, rufen Sie die CheckDeviceState Methode auf, da die TestCooperativeLevel Methode veraltet ist und immer Erfolg zurückgibt. Wenn das Benutzergerät nicht verfügbar ist, rufen Sie SetBackBuffer auf, um den WPF-Verweis auf den Hintergrundpuffer freizugeben. Wenn Sie Ihr Gerät zurücksetzen müssen, rufen Sie SetBackBuffer auf, wobei der Parameter backBuffer auf null gesetzt ist, und rufen Sie SetBackBuffer anschließend erneut auf, wobei backBuffer auf eine gültige Direct3D-Oberfläche gesetzt ist.
Rufen Sie die Reset Methode auf, um von einem ungültigen Gerät wiederherzustellen, nur wenn Sie die Unterstützung für mehrere Adapter implementieren. Lassen Sie andernfalls alle Direct3D9-Schnittstellen frei, und erstellen Sie sie vollständig neu. Wenn sich das Adapterlayout geändert hat, werden direct3D9-Objekte, die vor der Änderung erstellt wurden, nicht aktualisiert.
Umgang mit Größenänderungen
Wenn eine D3DImage in einer anderen Auflösung als ihrer natürlichen Größe angezeigt wird, wird sie entsprechend der aktuellen BitmapScalingMode skaliert, mit der Ausnahme, dass sie anstelle von Bilinear durch Fant ersetzt wird.
Wenn Sie eine höhere Fideliät benötigen, müssen Sie eine neue Oberfläche erstellen, wenn sich die Größe des D3DImage ändert.
Es gibt drei mögliche Ansätze zum Behandeln der Größenänderung.
Nehmen Sie am Layoutsystem teil, und erstellen Sie eine neue Oberfläche, wenn sich die Größe ändert. Erstellen Sie nicht zu viele Oberflächen, da Sie möglicherweise den Videospeicher ausschöpfen oder fragmentieren.
Warten Sie, bis ein Größenänderungsereignis für einen festen Zeitraum nicht aufgetreten ist, um die neue Oberfläche zu erstellen.
Erstellen Sie ein DispatcherTimer Objekt, das die Containerabmessungen mehrmals pro Sekunde überprüft.
Optimierung mit mehreren Monitoren
Die Leistung kann erheblich reduziert werden, wenn das Renderingsystem einen auf einen D3DImage anderen Monitor verschiebt.
Auf WDDM, solange sich die Monitore auf derselben Grafikkarte befinden und Sie verwenden Direct3DCreate9Ex, gibt es keine Reduzierung der Leistung. Wenn sich die Monitore auf separaten Grafikkarten befinden, wird die Leistung reduziert. Unter Windows XP wird die Leistung immer reduziert.
Wenn der D3DImage zu einem anderen Monitor wechselt, können Sie auf dem entsprechenden Adapter eine neue Oberfläche erstellen, um eine gute Leistung wiederherzustellen.
Um die Leistungseinbußen zu vermeiden, schreiben Sie Code speziell für den Fall mit mehreren Monitoren. Die folgende Liste zeigt eine Möglichkeit zum Schreiben von Multimonitorcode.
Suchen Sie einen Punkt im D3DImage Bildschirmbereich mit der
Visual.ProjectToScreenMethode.Verwenden Sie die
MonitorFromPointGDI-Methode, um den Monitor zu finden, der den Punkt anzeigt.Verwenden Sie die
IDirect3D9::GetAdapterMonitorMethode, um zu ermitteln, auf welchem Direct3D9-Adapter sich der Monitor befindet.Wenn der Adapter nicht mit dem Adapter mit dem Hintergrundpuffer übereinstimmt, erstellen Sie einen neuen Hintergrundpuffer auf dem neuen Monitor, und weisen Sie ihn dem D3DImage Hintergrundpuffer zu.
Hinweis
Wenn D3DImage sich über mehrere Monitoren erstreckt, wird die Leistung langsam, außer im Fall von WDDM und IDirect3D9Ex auf demselben Adapter. Es gibt keine Möglichkeit, die Leistung in dieser Situation zu verbessern.
Das folgende Codebeispiel zeigt, wie sie den aktuellen Monitor finden.
void
CRendererManager::SetAdapter(POINT screenSpacePoint)
{
CleanupInvalidDevices();
//
// After CleanupInvalidDevices, we may not have any D3D objects. Rather than
// recreate them here, ignore the adapter update and wait for render to recreate.
//
if (m_pD3D && m_rgRenderers)
{
HMONITOR hMon = MonitorFromPoint(screenSpacePoint, MONITOR_DEFAULTTONULL);
for (UINT i = 0; i < m_cAdapters; ++i)
{
if (hMon == m_pD3D->GetAdapterMonitor(i))
{
m_pCurrentRenderer = m_rgRenderers[i];
break;
}
}
}
}
Aktualisieren Sie den Monitor, wenn sich die Größe oder Position des D3DImage Containers ändert, oder aktualisieren Sie den Monitor mithilfe einer DispatcherTimer, die ein paar Mal pro Sekunde aktualisiert wird.
WPF-Softwarerendering
WPF wird synchron auf dem UI-Thread der Software in den folgenden Situationen gerendert.
Wenn eine dieser Situationen auftritt, ruft das Renderingsystem die CopyBackBuffer Methode auf, um den Hardwarepuffer in Software zu kopieren. Die Standardimplementierung ruft die GetRenderTargetData Methode mit Ihrer Oberfläche auf. Da dieser Aufruf außerhalb des Sperr-/Entsperrmusters auftritt, schlägt er möglicherweise fehl. In diesem Fall gibt die CopyBackBuffer-Methode null zurück, und es wird kein Bild angezeigt.
Sie können die CopyBackBuffer-Methode überschreiben, die Basisimplementierung aufrufen und, wenn sie null zurückgibt, einen Platzhalter BitmapSource zurückgeben.
Sie können auch ihr eigenes Softwarerendering implementieren, anstatt die Basisimplementierung aufzurufen.
Hinweis
Wenn WPF vollständig in Software gerendert wird, wird D3DImage nicht angezeigt, da WPF keinen Frontpuffer hat.
Siehe auch
.NET Desktop feedback