Systemeigene Zusammenarbeit mit DirectX und Direct2D
Die Windows.UI.Composition-API stellt die nativen Interoperationsschnittstellen "ICompositorInterop", "ICompositionDrawingSurfaceInterop" und "ICompositionGraphicsDeviceInterop" bereit, sodass Inhalte direkt in den Kompositor verschoben werden können.
Die systemeigene Interoperabilität ist um Oberflächenobjekte strukturiert, die von DirectX-Texturen unterstützt werden. Die Oberflächen werden aus einem Factoryobjekt namens CompositionGraphicsDevice erstellt. Dieses Objekt wird von einem zugrunde liegenden Direct2D- oder Direct3D-Geräteobjekt unterstützt, das zum Zuweisen des Videospeichers für Oberflächen verwendet wird. Die Kompositions-API erstellt nie das zugrunde liegende DirectX-Gerät. Es liegt in der Verantwortung der Anwendung, eine zu erstellen und an das CompositionGraphicsDevice-Objekt zu übergeben. Eine Anwendung kann mehrere CompositionGraphicsDevice-Objekte gleichzeitig erstellen und kann dasselbe DirectX-Gerät wie das Renderinggerät für mehrere CompositionGraphicsDevice-Objekte verwenden.
Erstellen einer Oberfläche
Jedes CompositionGraphicsDevice dient als Oberflächenfabrik. Jede Oberfläche wird mit einer Anfangsgröße (möglicherweise 0,0) erstellt, aber keine gültigen Pixel. Eine Oberfläche im Anfangszustand kann in einer visuellen Struktur, z. B. über einen CompositionSurfaceBrush und einen SpriteVisual, sofort genutzt werden, aber in ihrem Anfangszustand hat die Oberfläche keine Auswirkungen auf die Bildschirmausgabe. Es ist für alle Zwecke vollständig transparent, auch wenn der angegebene Alphamodus "undurchsichtig" ist.
Gelegentlich können DirectX-Geräte nicht verwendet werden. Dies kann unter anderem der Fall sein, wenn die Anwendung ungültige Argumente an bestimmte DirectX-APIs übergibt oder wenn der Grafikkarte vom System zurückgesetzt wird oder wenn der Treiber aktualisiert wird. Direct3D verfügt über eine API, die eine Anwendung verwenden kann, um asynchron zu ermitteln, wenn das Gerät aus irgendeinem Grund verloren geht. Wenn ein DirectX-Gerät verloren geht, muss die Anwendung es verwerfen, eine neue erstellen und an alle CompositionGraphicsDevice-Objekte übergeben, die zuvor dem ungültigen DirectX-Gerät zugeordnet sind.
Laden von Pixeln in eine Oberfläche
Um Pixel in die Oberfläche zu laden, muss die Anwendung die BeginDraw-Methode aufrufen, die eine DirectX-Schnittstelle zurückgibt, die eine Textur oder einen Direct2D-Kontext darstellt, je nachdem, was die Anwendung anfordert. Die Anwendung muss dann Pixel in diese Textur rendern oder hochladen. Wenn die Anwendung abgeschlossen ist, muss sie die EndDraw-Methode aufrufen. Nur zu diesem Zeitpunkt sind die neuen Pixel für die Komposition verfügbar, aber sie werden immer noch nicht auf dem Bildschirm angezeigt, bis alle Änderungen an der visuellen Struktur das nächste Mal zugesichert werden. Wenn die visuelle Struktur vor dem Aufruf von EndDraw zugesichert wird, ist das update, das gerade ausgeführt wird, nicht auf dem Bildschirm sichtbar, und die Oberfläche zeigt weiterhin den Inhalt an, den sie vor BeginDraw hatte. Wenn EndDraw aufgerufen wird, wird der von BeginDraw zurückgegebene Textur- oder Direct2D-Kontextzeiger ungültig. Eine Anwendung sollte diesen Zeiger niemals über den EndDraw-Aufruf hinaus zwischenspeichern.
Die Anwendung kann "BeginDraw" nur auf einer Oberfläche gleichzeitig für eine bestimmte CompositionGraphicsDevice aufrufen. Nach dem Aufrufen von BeginDraw muss die Anwendung EndDraw auf dieser Oberfläche aufrufen, bevor BeginDraw auf einer anderen aufgerufen wird. Da die API agil ist, ist die Anwendung für die Synchronisierung dieser Aufrufe verantwortlich, wenn sie rendering von mehreren Arbeitsthreads ausführen möchte. Wenn eine Anwendung das Rendern einer Oberfläche unterbrechen und vorübergehend zu einer anderen wechseln möchte, kann die Anwendung die SuspendDraw-Methode verwenden. Dadurch kann ein weiterer BeginDraw erfolgreich sein, aber das erste Oberflächenupdate nicht für die Komposition auf dem Bildschirm verfügbar machen. Auf diese Weise kann die Anwendung mehrere Aktualisierungen auf transaktionsrelevante Weise ausführen. Sobald eine Oberfläche angehalten wurde, kann die Anwendung das Update fortsetzen, indem sie die ResumeDraw-Methode aufruft, oder sie deklariert, dass das Update durch Aufrufen von EndDraw erfolgt. Dies bedeutet, dass jeweils nur eine Oberfläche für eine beliebige CompositionGraphicsDevice aktiv aktualisiert werden kann. Jedes Grafikgerät behält diesen Zustand unabhängig von den anderen, sodass eine Anwendung auf zwei Oberflächen gleichzeitig gerendert wird, wenn sie zu verschiedenen Grafikgeräten gehören. Dies schließt jedoch aus, dass der Videospeicher für diese beiden Oberflächen zusammengepoolt wird und daher weniger Arbeitsspeicher effizient ist.
Die Methoden BeginDraw, SuspendDraw, ResumeDraw und EndDraw geben Fehler zurück, wenn die Anwendung einen falschen Vorgang ausführt (z. B. das Übergeben ungültiger Argumente oder das Aufrufen von BeginDraw auf einer Oberfläche vor dem Aufrufen von EndDraw auf einem anderen). Diese Arten von Fehlern stellen Anwendungsfehler dar, und daher wird erwartet, dass sie mit einem Fehler schnell behandelt werden. BeginDraw kann auch einen Fehler zurückgeben, wenn das zugrunde liegende DirectX-Gerät verloren geht. Dieser Fehler ist nicht schwerwiegend, da die Anwendung das DirectX-Gerät neu erstellen und es erneut versuchen kann. Daher wird erwartet, dass die Anwendung geräteverlusten durch einfaches Überspringen des Renderings verarbeitet. Wenn BeginDraw aus irgendeinem Grund fehlschlägt, sollte die Anwendung auch "EndDraw" nicht aufrufen, da der Anfang nie erfolgreich war.
Scrollen
Wenn eine Anwendung BeginDraw aufruft, ist aus Leistungsgründen nicht garantiert, dass der Inhalt der zurückgegebenen Textur der vorherige Inhalt der Oberfläche ist. Die Anwendung muss davon ausgehen, dass der Inhalt zufällig ist, und die Anwendung muss sicherstellen, dass alle Pixel berührt werden, indem sie die Oberfläche vor dem Rendern löschen oder genügend undurchsichtige Inhalte zeichnen, um das gesamte aktualisierte Rechteck abzudecken. Dies in Kombination mit der Tatsache, dass der Texturzeiger nur zwischen BeginDraw - und EndDraw-Aufrufen gültig ist, macht es der Anwendung unmöglich, vorherige Inhalte aus der Oberfläche zu kopieren. Aus diesem Grund bieten wir eine Scroll-Methode an, mit der die Anwendung eine Pixelkopie mit derselben Oberfläche durchführen kann.
C++/WinRT-Verwendungsbeispiel
Im folgenden Codebeispiel wird ein Interoperabilitätsszenario veranschaulicht. Das Beispiel kombiniert Typen aus dem Windows-Runtime-basierten Oberflächenbereich von Windows Composition zusammen mit Typen aus den Interoperabilitätsheadern und Code, der Text mithilfe der COM-basierten DirectWrite- und Direct2D-APIs rendert. Im Beispiel wird BeginDraw und EndDraw verwendet, um die Zusammenarbeit zwischen diesen Technologien nahtlos zu gestalten. Im Beispiel wird DirectWrite verwendet, um den Text zu gestalten, und anschließend wird Direct2D zum Rendern verwendet. Das Grafikgerät für die Komposition akzeptiert das Direct2D-Gerät direkt zur Initialisierungszeit. Dadurch kann BeginDraw einen ID2D1DeviceContext-Schnittstellenzeiger zurückgeben, der wesentlich effizienter ist als die Erstellung eines Direct2D-Kontexts zum Umbrechen einer zurückgegebenen ID3D11Texture2D-Schnittstelle bei jedem Zeichnungsvorgang.
Um das folgende C++/WinRT-Codebeispiel auszuprobieren, erstellen Sie zuerst ein neues Core-App-Projekt (C++/WinRT) in Visual Studio (für Anforderungen finden Sie unter Visual Studio-Unterstützung für C++/WinRT). Ersetzen Sie den Inhalt Ihrer pch.h
Und App.cpp
Quellcodedateien durch die folgenden Codeauflistungen, und führen Sie sie aus. Die Anwendung rendert die Zeichenfolge "Hello, World!" in schwarzem Text auf einem transparenten Hintergrund.
// pch.h
#pragma once
#include <windows.h>
#include <D2d1_1.h>
#include <D3d11_4.h>
#include <Dwrite.h>
#include <Windows.Graphics.DirectX.Direct3D11.interop.h>
#include <Windows.ui.composition.interop.h>
#include <unknwn.h>
#include <winrt/Windows.ApplicationModel.Core.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Graphics.DirectX.h>
#include <winrt/Windows.Graphics.DirectX.Direct3D11.h>
#include <winrt/Windows.UI.Composition.h>
#include <winrt/Windows.UI.Core.h>
#include <winrt/Windows.UI.Input.h>
// App.cpp
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//*********************************************************
#include "pch.h"
using namespace winrt;
using namespace winrt::Windows::ApplicationModel::Core;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Numerics;
using namespace winrt::Windows::Graphics::DirectX;
using namespace winrt::Windows::Graphics::DirectX::Direct3D11;
using namespace winrt::Windows::UI;
using namespace winrt::Windows::UI::Composition;
using namespace winrt::Windows::UI::Core;
namespace abi
{
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Graphics::DirectX;
using namespace ABI::Windows::UI::Composition;
}
// An app-provided helper to render lines of text.
struct SampleText
{
SampleText(winrt::com_ptr<::IDWriteTextLayout> const& text, CompositionGraphicsDevice const& compositionGraphicsDevice) :
m_text(text),
m_compositionGraphicsDevice(compositionGraphicsDevice)
{
// Create the surface just big enough to hold the formatted text block.
DWRITE_TEXT_METRICS metrics;
winrt::check_hresult(m_text->GetMetrics(&metrics));
winrt::Windows::Foundation::Size surfaceSize{ metrics.width, metrics.height };
CompositionDrawingSurface drawingSurface{ m_compositionGraphicsDevice.CreateDrawingSurface(
surfaceSize,
DirectXPixelFormat::B8G8R8A8UIntNormalized,
DirectXAlphaMode::Premultiplied) };
// Cache the interop pointer, since that's what we always use.
m_drawingSurfaceInterop = drawingSurface.as<abi::ICompositionDrawingSurfaceInterop>();
// Draw the text
DrawText();
// If the rendering device is lost, the application will recreate and replace it. We then
// own redrawing our pixels.
m_deviceReplacedEventToken = m_compositionGraphicsDevice.RenderingDeviceReplaced(
[this](CompositionGraphicsDevice const&, RenderingDeviceReplacedEventArgs const&)
{
// Draw the text again.
DrawText();
return S_OK;
});
}
~SampleText()
{
m_compositionGraphicsDevice.RenderingDeviceReplaced(m_deviceReplacedEventToken);
}
// Return the underlying surface to the caller.
auto Surface()
{
// To the caller, the fact that we have a drawing surface is an implementation detail.
// Return the base interface instead.
return m_drawingSurfaceInterop.as<ICompositionSurface>();
}
private:
// The text to draw.
winrt::com_ptr<::IDWriteTextLayout> m_text;
// The composition surface that we use in the visual tree.
winrt::com_ptr<abi::ICompositionDrawingSurfaceInterop> m_drawingSurfaceInterop;
// The device that owns the surface.
CompositionGraphicsDevice m_compositionGraphicsDevice{ nullptr };
//winrt::com_ptr<abi::ICompositionGraphicsDevice> m_compositionGraphicsDevice2;
// For managing our event notifier.
winrt::event_token m_deviceReplacedEventToken;
// We may detect device loss on BeginDraw calls. This helper handles this condition or other
// errors.
bool CheckForDeviceRemoved(HRESULT hr)
{
if (hr == S_OK)
{
// Everything is fine: go ahead and draw.
return true;
}
if (hr == DXGI_ERROR_DEVICE_REMOVED)
{
// We can't draw at this time, but this failure is recoverable. Just skip drawing for
// now. We will be asked to draw again once the Direct3D device is recreated.
return false;
}
// Any other error is unexpected and, therefore, fatal.
winrt::check_hresult(hr);
return true;
}
// Renders the text into our composition surface
void DrawText()
{
// Begin our update of the surface pixels. If this is our first update, we are required
// to specify the entire surface, which nullptr is shorthand for (but, as it works out,
// any time we make an update we touch the entire surface, so we always pass nullptr).
winrt::com_ptr<::ID2D1DeviceContext> d2dDeviceContext;
POINT offset;
if (CheckForDeviceRemoved(m_drawingSurfaceInterop->BeginDraw(nullptr,
__uuidof(ID2D1DeviceContext), d2dDeviceContext.put_void(), &offset)))
{
d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Black, 0.f));
// Create a solid color brush for the text. A more sophisticated application might want
// to cache and reuse a brush across all text elements instead, taking care to recreate
// it in the event of device removed.
winrt::com_ptr<::ID2D1SolidColorBrush> brush;
winrt::check_hresult(d2dDeviceContext->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Black, 1.0f), brush.put()));
// Draw the line of text at the specified offset, which corresponds to the top-left
// corner of our drawing surface. Notice we don't call BeginDraw on the D2D device
// context; this has already been done for us by the composition API.
d2dDeviceContext->DrawTextLayout(D2D1::Point2F((float)offset.x, (float)offset.y), m_text.get(),
brush.get());
// Our update is done. EndDraw never indicates rendering device removed, so any
// failure here is unexpected and, therefore, fatal.
winrt::check_hresult(m_drawingSurfaceInterop->EndDraw());
}
}
};
struct DeviceLostEventArgs
{
DeviceLostEventArgs(IDirect3DDevice const& device) : m_device(device) {}
IDirect3DDevice Device() { return m_device; }
static DeviceLostEventArgs Create(IDirect3DDevice const& device) { return DeviceLostEventArgs{ device }; }
private:
IDirect3DDevice m_device;
};
struct DeviceLostHelper
{
DeviceLostHelper() = default;
~DeviceLostHelper()
{
StopWatchingCurrentDevice();
m_onDeviceLostHandler = nullptr;
}
IDirect3DDevice CurrentlyWatchedDevice() { return m_device; }
void WatchDevice(winrt::com_ptr<::IDXGIDevice> const& dxgiDevice)
{
// If we're currently listening to a device, then stop.
StopWatchingCurrentDevice();
// Set the current device to the new device.
m_device = nullptr;
winrt::check_hresult(::CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice.get(), reinterpret_cast<::IInspectable**>(winrt::put_abi(m_device))));
// Get the DXGI Device.
m_dxgiDevice = dxgiDevice;
// QI For the ID3D11Device4 interface.
winrt::com_ptr<::ID3D11Device4> d3dDevice{ m_dxgiDevice.as<::ID3D11Device4>() };
// Create a wait struct.
m_onDeviceLostHandler = nullptr;
m_onDeviceLostHandler = ::CreateThreadpoolWait(DeviceLostHelper::OnDeviceLost, (PVOID)this, nullptr);
// Create a handle and a cookie.
m_eventHandle.attach(::CreateEvent(nullptr, false, false, nullptr));
winrt::check_bool(bool{ m_eventHandle });
m_cookie = 0;
// Register for device lost.
::SetThreadpoolWait(m_onDeviceLostHandler, m_eventHandle.get(), nullptr);
winrt::check_hresult(d3dDevice->RegisterDeviceRemovedEvent(m_eventHandle.get(), &m_cookie));
}
void StopWatchingCurrentDevice()
{
if (m_dxgiDevice)
{
// QI For the ID3D11Device4 interface.
auto d3dDevice{ m_dxgiDevice.as<::ID3D11Device4>() };
// Unregister from the device lost event.
::CloseThreadpoolWait(m_onDeviceLostHandler);
d3dDevice->UnregisterDeviceRemoved(m_cookie);
// Clear member variables.
m_onDeviceLostHandler = nullptr;
m_eventHandle.close();
m_cookie = 0;
m_device = nullptr;
}
}
void DeviceLost(winrt::delegate<DeviceLostHelper const*, DeviceLostEventArgs const&> const& handler)
{
m_deviceLost = handler;
}
winrt::delegate<DeviceLostHelper const*, DeviceLostEventArgs const&> m_deviceLost;
private:
void RaiseDeviceLostEvent(IDirect3DDevice const& oldDevice)
{
m_deviceLost(this, DeviceLostEventArgs::Create(oldDevice));
}
static void CALLBACK OnDeviceLost(PTP_CALLBACK_INSTANCE /* instance */, PVOID context, PTP_WAIT /* wait */, TP_WAIT_RESULT /* waitResult */)
{
auto deviceLostHelper = reinterpret_cast<DeviceLostHelper*>(context);
auto oldDevice = deviceLostHelper->m_device;
deviceLostHelper->StopWatchingCurrentDevice();
deviceLostHelper->RaiseDeviceLostEvent(oldDevice);
}
private:
IDirect3DDevice m_device;
winrt::com_ptr<::IDXGIDevice> m_dxgiDevice;
PTP_WAIT m_onDeviceLostHandler{ nullptr };
winrt::handle m_eventHandle;
DWORD m_cookie{ 0 };
};
struct SampleApp : implements<SampleApp, IFrameworkViewSource, IFrameworkView>
{
IFrameworkView CreateView()
{
return *this;
}
void Initialize(CoreApplicationView const&)
{
}
// Run once when the application starts up
void Initialize()
{
// Create a Direct2D device.
CreateDirect2DDevice();
// To create a composition graphics device, we need to QI for another interface
winrt::com_ptr<abi::ICompositorInterop> compositorInterop{ m_compositor.as<abi::ICompositorInterop>() };
// Create a graphics device backed by our D3D device
winrt::com_ptr<abi::ICompositionGraphicsDevice> compositionGraphicsDeviceIface;
winrt::check_hresult(compositorInterop->CreateGraphicsDevice(
m_d2dDevice.get(),
compositionGraphicsDeviceIface.put()));
m_compositionGraphicsDevice = compositionGraphicsDeviceIface.as<CompositionGraphicsDevice>();
}
void Load(hstring const&)
{
}
void Uninitialize()
{
}
void Run()
{
CoreWindow window = CoreWindow::GetForCurrentThread();
window.Activate();
CoreDispatcher dispatcher = window.Dispatcher();
dispatcher.ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit);
}
void SetWindow(CoreWindow const& window)
{
m_compositor = Compositor{};
m_target = m_compositor.CreateTargetForCurrentView();
ContainerVisual root = m_compositor.CreateContainerVisual();
m_target.Root(root);
Initialize();
winrt::check_hresult(
::DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(m_dWriteFactory),
reinterpret_cast<::IUnknown**>(m_dWriteFactory.put())
)
);
winrt::check_hresult(
m_dWriteFactory->CreateTextFormat(
L"Segoe UI",
nullptr,
DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
36.f,
L"en-US",
m_textFormat.put()
)
);
Rect windowBounds{ window.Bounds() };
std::wstring text{ L"Hello, World!" };
winrt::check_hresult(
m_dWriteFactory->CreateTextLayout(
text.c_str(),
(uint32_t)text.size(),
m_textFormat.get(),
windowBounds.Width,
windowBounds.Height,
m_textLayout.put()
)
);
Visual textVisual{ CreateVisualFromTextLayout(m_textLayout) };
textVisual.Size({ windowBounds.Width, windowBounds.Height });
root.Children().InsertAtTop(textVisual);
}
// Called when Direct3D signals the device lost event.
void OnDirect3DDeviceLost(DeviceLostHelper const* /* sender */, DeviceLostEventArgs const& /* args */)
{
// Create a new Direct2D device.
CreateDirect2DDevice();
// Restore our composition graphics device to good health.
winrt::com_ptr<abi::ICompositionGraphicsDeviceInterop> compositionGraphicsDeviceInterop{ m_compositionGraphicsDevice.as<abi::ICompositionGraphicsDeviceInterop>() };
winrt::check_hresult(compositionGraphicsDeviceInterop->SetRenderingDevice(m_d2dDevice.get()));
}
// Create a surface that is asynchronously filled with an image
ICompositionSurface CreateSurfaceFromTextLayout(winrt::com_ptr<::IDWriteTextLayout> const& text)
{
// Create our wrapper object that will handle downloading and decoding the image (assume
// throwing new here).
SampleText textSurface{ text, m_compositionGraphicsDevice };
// The caller is only interested in the underlying surface.
return textSurface.Surface();
}
// Create a visual that holds an image.
Visual CreateVisualFromTextLayout(winrt::com_ptr<::IDWriteTextLayout> const& text)
{
// Create a sprite visual
SpriteVisual spriteVisual{ m_compositor.CreateSpriteVisual() };
// The sprite visual needs a brush to hold the image.
CompositionSurfaceBrush surfaceBrush{
m_compositor.CreateSurfaceBrush(CreateSurfaceFromTextLayout(text))
};
// Associate the brush with the visual.
CompositionBrush brush{ surfaceBrush.as<CompositionBrush>() };
spriteVisual.Brush(brush);
// Return the visual to the caller as an IVisual.
return spriteVisual;
}
private:
CompositionTarget m_target{ nullptr };
Compositor m_compositor{ nullptr };
winrt::com_ptr<::ID2D1Device> m_d2dDevice;
winrt::com_ptr<::IDXGIDevice> m_dxgiDevice;
//winrt::com_ptr<abi::ICompositionGraphicsDevice> m_compositionGraphicsDevice;
CompositionGraphicsDevice m_compositionGraphicsDevice{ nullptr };
std::vector<SampleText> m_textSurfaces;
DeviceLostHelper m_deviceLostHelper;
winrt::com_ptr<::IDWriteFactory> m_dWriteFactory;
winrt::com_ptr<::IDWriteTextFormat> m_textFormat;
winrt::com_ptr<::IDWriteTextLayout> m_textLayout;
// This helper creates a Direct2D device, and registers for a device loss
// notification on the underlying Direct3D device. When that notification is
// raised, the OnDirect3DDeviceLost method is called.
void CreateDirect2DDevice()
{
uint32_t createDeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
// Array with DirectX hardware feature levels in order of preference.
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
// Create the Direct3D 11 API device object and a corresponding context.
winrt::com_ptr<::ID3D11Device> d3DDevice;
winrt::com_ptr<::ID3D11DeviceContext> d3DImmediateContext;
D3D_FEATURE_LEVEL d3dFeatureLevel{ D3D_FEATURE_LEVEL_9_1 };
winrt::check_hresult(
::D3D11CreateDevice(
nullptr, // Default adapter.
D3D_DRIVER_TYPE_HARDWARE,
0, // Not asking for a software driver, so not passing a module to one.
createDeviceFlags, // Set debug and Direct2D compatibility flags.
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
d3DDevice.put(),
&d3dFeatureLevel,
d3DImmediateContext.put()
)
);
// Initialize Direct2D resources.
D2D1_FACTORY_OPTIONS d2d1FactoryOptions{ D2D1_DEBUG_LEVEL_NONE };
// Initialize the Direct2D Factory.
winrt::com_ptr<::ID2D1Factory1> d2D1Factory;
winrt::check_hresult(
::D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(d2D1Factory),
&d2d1FactoryOptions,
d2D1Factory.put_void()
)
);
// Create the Direct2D device object.
// Obtain the underlying DXGI device of the Direct3D device.
m_dxgiDevice = d3DDevice.as<::IDXGIDevice>();
m_d2dDevice = nullptr;
winrt::check_hresult(
d2D1Factory->CreateDevice(m_dxgiDevice.get(), m_d2dDevice.put())
);
m_deviceLostHelper.WatchDevice(m_dxgiDevice);
m_deviceLostHelper.DeviceLost({ this, &SampleApp::OnDirect3DDeviceLost });
}
};
int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
CoreApplication::Run(winrt::make<SampleApp>());
}
C++/CX-Verwendungsbeispiel
Hinweis
Dieses Codebeispiel ist vorhanden, um Ihre C++/CX-Anwendung zu verwalten. Wir empfehlen aber, C++/WinRT für neue Anwendungen zu nutzen. C++/WinRT ist eine vollständig standardisierte, moderne C++17-Programmiersprache für Windows-Runtime-APIs (WinRT), die als headerdateibasierte Bibliothek implementiert ist und Ihnen einen erstklassigen Zugriff auf die moderne Windows-API bietet.
Im folgenden C++/CX-Codebeispiel werden die DirectWrite- und Direct2D-Teile des Beispiels weggelassen.
//------------------------------------------------------------------------------
//
// Copyright (C) Microsoft. All rights reserved.
//
//------------------------------------------------------------------------------
#include "stdafx.h"
using namespace Microsoft::WRL;
using namespace Windows::Foundation;
using namespace Windows::Graphics::DirectX;
using namespace Windows::UI::Composition;
// This is an app-provided helper to render lines of text
class SampleText
{
private:
// The text to draw
ComPtr<IDWriteTextLayout> _text;
// The composition surface that we use in the visual tree
ComPtr<ICompositionDrawingSurfaceInterop> _drawingSurfaceInterop;
// The device that owns the surface
ComPtr<ICompositionGraphicsDevice> _compositionGraphicsDevice;
// For managing our event notifier
EventRegistrationToken _deviceReplacedEventToken;
public:
SampleText(IDWriteTextLayout* text, ICompositionGraphicsDevice* compositionGraphicsDevice) :
_text(text),
_compositionGraphicsDevice(compositionGraphicsDevice)
{
// Create the surface just big enough to hold the formatted text block.
DWRITE_TEXT_METRICS metrics;
FailFastOnFailure(text->GetMetrics(&metrics));
Windows::Foundation::Size surfaceSize = { metrics.width, metrics.height };
ComPtr<ICompositionDrawingSurface> drawingSurface;
FailFastOnFailure(_compositionGraphicsDevice->CreateDrawingSurface(
surfaceSize,
DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized,
DirectXAlphaMode::DirectXAlphaMode_Ignore,
&drawingSurface));
// Cache the interop pointer, since that's what we always use.
FailFastOnFailure(drawingSurface.As(&_drawingSurfaceInterop));
// Draw the text
DrawText();
// If the rendering device is lost, the application will recreate and replace it. We then
// own redrawing our pixels.
FailFastOnFailure(_compositionGraphicsDevice->add_RenderingDeviceReplaced(
Callback<RenderingDeviceReplacedEventHandler>([this](
ICompositionGraphicsDevice* source, IRenderingDeviceReplacedEventArgs* args)
-> HRESULT
{
// Draw the text again
DrawText();
return S_OK;
}).Get(),
&_deviceReplacedEventToken));
}
~SampleText()
{
FailFastOnFailure(_compositionGraphicsDevice->remove_RenderingDeviceReplaced(
_deviceReplacedEventToken));
}
// Return the underlying surface to the caller
ComPtr<ICompositionSurface> get_Surface()
{
// To the caller, the fact that we have a drawing surface is an implementation detail.
// Return the base interface instead
ComPtr<ICompositionSurface> surface;
FailFastOnFailure(_drawingSurfaceInterop.As(&surface));
return surface;
}
private:
// We may detect device loss on BeginDraw calls. This helper handles this condition or other
// errors.
bool CheckForDeviceRemoved(HRESULT hr)
{
if (SUCCEEDED(hr))
{
// Everything is fine -- go ahead and draw
return true;
}
else if (hr == DXGI_ERROR_DEVICE_REMOVED)
{
// We can't draw at this time, but this failure is recoverable. Just skip drawing for
// now. We will be asked to draw again once the Direct3D device is recreated
return false;
}
else
{
// Any other error is unexpected and, therefore, fatal
FailFast();
}
}
// Renders the text into our composition surface
void DrawText()
{
// Begin our update of the surface pixels. If this is our first update, we are required
// to specify the entire surface, which nullptr is shorthand for (but, as it works out,
// any time we make an update we touch the entire surface, so we always pass nullptr).
ComPtr<ID2D1DeviceContext> d2dDeviceContext;
POINT offset;
if (CheckForDeviceRemoved(_drawingSurfaceInterop->BeginDraw(nullptr,
__uuidof(ID2D1DeviceContext), &d2dDeviceContext, &offset)))
{
// Create a solid color brush for the text. A more sophisticated application might want
// to cache and reuse a brush across all text elements instead, taking care to recreate
// it in the event of device removed.
ComPtr<ID2D1SolidColorBrush> brush;
FailFastOnFailure(d2dDeviceContext->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Black, 1.0f), &brush));
// Draw the line of text at the specified offset, which corresponds to the top-left
// corner of our drawing surface. Notice we don't call BeginDraw on the D2D device
// context; this has already been done for us by the composition API.
d2dDeviceContext->DrawTextLayout(D2D1::Point2F(offset.x, offset.y), _text.Get(),
brush.Get());
// Our update is done. EndDraw never indicates rendering device removed, so any
// failure here is unexpected and, therefore, fatal.
FailFastOnFailure(_drawingSurfaceInterop->EndDraw());
}
}
};
class SampleApp
{
ComPtr<ICompositor> _compositor;
ComPtr<ID2D1Device> _d2dDevice;
ComPtr<ICompositionGraphicsDevice> _compositionGraphicsDevice;
std::vector<ComPtr<SampleText>> _textSurfaces;
public:
// Run once when the application starts up
void Initialize(ICompositor* compositor)
{
// Cache the compositor (created outside of this method)
_compositor = compositor;
// Create a Direct2D device (helper implementation not shown here)
FailFastOnFailure(CreateDirect2DDevice(&_d2dDevice));
// To create a composition graphics device, we need to QI for another interface
ComPtr<ICompositorInterop> compositorInterop;
FailFastOnFailure(_compositor.As(&compositorInterop));
// Create a graphics device backed by our D3D device
FailFastOnFailure(compositorInterop->CreateGraphicsDevice(
_d2dDevice.Get(),
&_compositionGraphicsDevice));
}
// Called when Direct3D signals the device lost event
void OnDirect3DDeviceLost()
{
// Create a new device
FailFastOnFailure(CreateDirect2DDevice(_d2dDevice.ReleaseAndGetAddressOf()));
// Restore our composition graphics device to good health
ComPtr<ICompositionGraphicsDeviceInterop> compositionGraphicsDeviceInterop;
FailFastOnFailure(_compositionGraphicsDevice.As(&compositionGraphicsDeviceInterop));
FailFastOnFailure(compositionGraphicsDeviceInterop->SetRenderingDevice(_d2dDevice.Get()));
}
// Create a surface that is asynchronously filled with an image
ComPtr<ICompositionSurface> CreateSurfaceFromTextLayout(IDWriteTextLayout* text)
{
// Create our wrapper object that will handle downloading and decoding the image (assume
// throwing new here)
SampleText* textSurface = new SampleText(text, _compositionGraphicsDevice.Get());
// Keep our image alive
_textSurfaces.push_back(textSurface);
// The caller is only interested in the underlying surface
return textSurface->get_Surface();
}
// Create a visual that holds an image
ComPtr<IVisual> CreateVisualFromTextLayout(IDWriteTextLayout* text)
{
// Create a sprite visual
ComPtr<ISpriteVisual> spriteVisual;
FailFastOnFailure(_compositor->CreateSpriteVisual(&spriteVisual));
// The sprite visual needs a brush to hold the image
ComPtr<ICompositionSurfaceBrush> surfaceBrush;
FailFastOnFailure(_compositor->CreateSurfaceBrushWithSurface(
CreateSurfaceFromTextLayout(text).Get(),
&surfaceBrush));
// Associate the brush with the visual
ComPtr<ICompositionBrush> brush;
FailFastOnFailure(surfaceBrush.As(&brush));
FailFastOnFailure(spriteVisual->put_Brush(brush.Get()));
// Return the visual to the caller as the base class
ComPtr<IVisual> visual;
FailFastOnFailure(spriteVisual.As(&visual));
return visual;
}
private:
// This helper (implementation not shown here) creates a Direct2D device and registers
// for a device loss notification on the underlying Direct3D device. When that notification is
// raised, assume the OnDirect3DDeviceLost method is called.
HRESULT CreateDirect2DDevice(ID2D1Device** ppDevice);
};