Interoperabilidad de DirectX y XAML
Puedes usar lenguaje XAML (XAML) junto con Microsoft DirectX juntos en tu juego o aplicación de plataforma Windows universal (UWP). La combinación de XAML y DirectX permite crear marcos de interfaz de usuario flexibles que interoperan con el contenido representado por DirectX. que es especialmente útil para aplicaciones con un uso intensivo de gráficos. En este tema se explica la estructura de una aplicación para UWP que usa DirectX e identifica los tipos importantes que se deben usar al compilar la aplicación para UWP para que funcione con DirectX.
Si la aplicación se centra principalmente en la representación en 2D, es posible que quiera usar la biblioteca Win2D Windows Runtime. Microsoft mantiene esa biblioteca y se basa en la tecnología direct2D principal. Win2D simplifica en gran medida el patrón de uso para implementar gráficos 2D e incluye abstracciones útiles para algunas de las técnicas descritas en este documento. Consulta la página del proyecto para obtener más detalles. Este documento ofrece orientación para aquellos desarrolladores de aplicaciones que decidan no utilizar Win2D.
Nota
Las API de DirectX no se definen como Windows Runtime, pero puedes usar C++/WinRT para desarrollar aplicaciones XAML para UWP que interoperan con DirectX. Si factormente el código que llama a DirectX en su propio componente de tiempo de ejecución de Windows (QR) de C++/WinRT, puedes usar ese ELEMENTO EN UNA APLICACIÓN PARA UWP (incluso una de C#) que, a continuación, combina XAML y DirectX.
XAML y DirectX
DirectX proporciona dos bibliotecas eficaces para gráficos 2D y 3D, respectivamente: Direct2D y Direct3D. Aunque XAML proporciona compatibilidad con primitivos y efectos 2D básicos, muchas aplicaciones de modelado y juegos necesitan una compatibilidad con gráficos más compleja. Para ello, puedes usar Direct2D y Direct3D para representar los gráficos más complejos y usar XAML para elementos de interfaz de usuario (UI) más tradicionales.
Si va a implementar la interoperabilidad XAML y DirectX personalizada, debe conocer estos dos conceptos.
- Las superficies compartidas son regiones de tamaño de la pantalla, definidas por XAML, en las que se puede usar DirectX para dibujar indirectamente mediante tipos Windows::UI::Xaml::Media::ImageSource. En el caso de las superficies compartidas, no se controla el momento preciso de cuándo aparece el nuevo contenido en pantalla. En su lugar, las actualizaciones de la superficie compartida se sincronizan con las actualizaciones del marco XAML.
- Una cadena de intercambio representa una colección de búferes que se usan para mostrar gráficos con una latencia mínima. Normalmente, una cadena de intercambio se actualiza a 60 fotogramas por segundo por separado del subproceso de la interfaz de usuario. Sin embargo, una cadena de intercambio usa más recursos de memoria y CPU para admitir actualizaciones rápidas y es relativamente difícil de usar, ya que tiene que administrar varios subprocesos.
Tenga en cuenta para qué está usando DirectX. ¿La usará para componer o animar un único control que se ajuste a las dimensiones de la ventana de presentación? ¿Contiene salida que tenga que representarse y controlarse en tiempo real, como sucede en un juego? Si es así, probablemente tendrá que implementar una cadena de intercambio. De lo contrario, debería estar bien usando una superficie compartida.
Una vez que haya determinado cómo piensa usar DirectX, usará uno de los siguientes tipos de Windows Runtime para incorporar la representación de DirectX en la aplicación para UWP.
- Si quieres crear una imagen estática o dibujar una imagen compleja a intervalos controlados por eventos, dibuja en una superficie compartida con Windows::UI::Xaml::Media::Imaging::SurfaceImageSource. Ese tipo controla una superficie de dibujo de DirectX de tamaño. Normalmente se usa este tipo al crear una imagen o textura, como un mapa de bits, que luego se mostrará en un documento o elemento de interfaz de usuario. Recuerda que no funciona bien con la interactividad en tiempo real como, por ejemplo, un juego de alto rendimiento. Esto se debe a que las actualizaciones de un objeto SurfaceImageSource se sincronizan con las actualizaciones de la interfaz de usuario XAML y eso puede introducir latencia en los comentarios visuales que se proporcionan al usuario, como una velocidad de fotogramas fluctuante o una respuesta deficiente percibida a la entrada en tiempo real. Sin embargo, las actualizaciones siguen siendo lo suficientemente rápidas para controles dinámicos o simulaciones de datos.
- Si la imagen es mayor que el espacio real de la pantalla proporcionado y el usuario puede desplazarse o ampliarla, use Windows::UI::Xaml::Media::Imaging::VirtualSurfaceImageSource. Ese tipo controla una superficie de dibujo de DirectX de tamaño mayor que la pantalla. Tal como sucede con SurfaceImageSource, usarás este tipo dinámicamente al componer una imagen o control complejos. Asimismo, al igual que SurfaceImageSource, este tipo no funciona bien en juegos de alto rendimiento. Algunos ejemplos de elementos XAML que pueden usar un tipo VirtualSurfaceImageSource, son los controles de mapa o un visor de documentos con imágenes grandes o de alta densidad.
- Si usa DirectX para presentar gráficos actualizados en tiempo real o en una situación en la que las actualizaciones deben incluirse en intervalos normales de baja latencia, use la clase SwapChainPanel para que pueda actualizar los gráficos sin sincronizar con el temporizador de actualización del marco XAML. Con SwapChainPanel puede acceder directamente a la cadena de intercambio del dispositivo gráfico(IDXGISwapChain1)y a la capa XAML sobre el destino de representación. SwapChainPanel funciona muy bien para juegos y aplicaciones DirectX de pantalla completa que requieren una interfaz de usuario basada en XAML. Debe conocer bien DirectX para usar este enfoque, incluidas las tecnologías microsoft Infraestructura de gráficos de DirectX (DXGI), Direct2D y Direct3D. Para obtener más información, vea Guía de programación para Direct3D 11.
SurfaceImageSource
SurfaceImageSource proporciona una superficie compartida de DirectX en la que dibujar; a continuación, crea los bits en el contenido de la aplicación.
Sugerencia
Las aplicaciones de ejemplo Espaciado de línea (DirectWrite) y Fuentes descargables (DirectWrite) muestran SurfaceImageSource.
En un nivel muy alto, este es el proceso para crear y actualizar un SurfaceImageSource.
- Cree un dispositivo Direct 3D, un dispositivo Direct 2D y un contexto de dispositivo Direct 2D.
- Cree un SurfaceImageSourcey establezca el dispositivo Direct 2D (o Direct 3D) en ese dispositivo.
- Comience a dibujar en SurfaceImageSource para obtener una superficie DXGI.
- Dibuje en la superficie DXGI con Direct2D (o Direct3D).
- Termine de dibujar en SurfaceImageSource cuando haya terminado.
- Establezca SurfaceImageSource en una imagen XAML oImageBrush para mostrarla en la interfaz de usuario xaml.
Y este es un análisis más profundo de esos pasos, con ejemplos de código fuente.
Puede seguir el código que se muestra y se describe a continuación mediante la creación de un nuevo proyecto en Microsoft Visual Studio. Crear un proyecto de aplicación en blanco (C++/WinRT). Elija como destino la versión más reciente disponible de manera general (es decir, no en versión preliminar) de Windows SDK.
Abra
pch.hy agreguepch.hsiguientes opciones que se incluyen debajo de las que ya existen.// pch.h ... #include <d3d11_4.h> #include <d2d1_1.h> #include <windows.ui.xaml.media.dxinterop.h> #include <winrt/Windows.UI.Xaml.Media.Imaging.h>Agregue la
usingdirectiva que se muestra a continuación en la parte superior de ,MainPage.cppdebajo de las que ya existen. También enMainPage.cpp, reemplace la implementación existente deMainPage.cpppor la lista que se muestra a continuación. El código crea un dispositivo Direct 3D, un dispositivo Direct 2D y un contexto de dispositivo Direct 2D. Para ello, llama a D3D11CreateDevice,D2D1CreateDevicee ID2D1Device::CreateDeviceContext.// MainPage.cpp | paste this below the existing using directives using namespace Windows::UI::Xaml::Media::Imaging;// MainPage.cpp | paste this to replace the existing MainPage::ClickHandler void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&) { myButton().Content(box_value(L"Clicked")); uint32_t creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; 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 device. winrt::com_ptr<::ID3D11Device> d3dDevice; D3D_FEATURE_LEVEL supportedFeatureLevel; winrt::check_hresult(::D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, creationFlags, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, d3dDevice.put(), &supportedFeatureLevel, nullptr) ); // Get the DXGI device. winrt::com_ptr<::IDXGIDevice> dxgiDevice{ d3dDevice.as<::IDXGIDevice>() }; // Create the Direct2D device and a corresponding context. winrt::com_ptr<::ID2D1Device> d2dDevice; ::D2D1CreateDevice(dxgiDevice.get(), nullptr, d2dDevice.put()); winrt::com_ptr<::ID2D1DeviceContext> d2dDeviceContext; winrt::check_hresult( d2dDevice->CreateDeviceContext( D2D1_DEVICE_CONTEXT_OPTIONS_NONE, d2dDeviceContext.put() ) ); }A continuación, agregue código para crear un SurfaceImageSourcey establezca el dispositivo Direct 2D (o Direct 3D) en ese mediante una llamada a ISurfaceImageSourceNativeWithD2D::SetDevice.
Nota
Si va a dibujar en SurfaceImageSource desde un subproceso en segundo plano, también deberá asegurarse de que el dispositivo DXGI tiene habilitado el acceso multiproceso (como se muestra en el código siguiente). Por motivos de rendimiento, solo debe hacerlo si va a dibujar desde un subproceso en segundo plano.
Definir el tamaño de la superficie compartida pasando los valores de alto y ancho al constructor SurfaceImageSource. También puedes indicar si la superficie necesita compatibilidad alfa (opacidad).
Para establecer el dispositivo y ejecutar las operaciones de dibujo, necesitamos un puntero a ISurfaceImageSourceNativeWithD2D. Para obtener uno, consulte el objeto SurfaceImageSource para su interfaz ISurfaceImageSourceNativeWithD2D subyacente.
// MainPage.cpp | paste this at the end of MainPage::ClickHandler SurfaceImageSource surfaceImageSource(500, 500); winrt::com_ptr<::ISurfaceImageSourceNativeWithD2D> sisNativeWithD2D{ surfaceImageSource.as<::ISurfaceImageSourceNativeWithD2D>() }; // Associate the Direct2D device with the SurfaceImageSource. sisNativeWithD2D->SetDevice(d2dDevice.get()); // To enable multi-threaded access (optional) winrt::com_ptr<::ID3D11Multithread> d3dMultiThread{ d3dDevice.as<::ID3D11Multithread>() }; d3dMultiThread->SetMultithreadProtected(true);Llame a ISurfaceImageSourceNativeWithD2D::BeginDraw para recuperar una superficie DXGI (una interfaz IDXGISurface). Puede llamar a ISurfaceImageSourceNativeWithD2D::BeginDraw (y los comandos de dibujo posteriores) desde un subproceso en segundo plano si ha habilitado el acceso multiproceso. En este paso también creará un mapa de bits a partir de la superficie DXGI y lo establecerá en el contexto del dispositivo Direct 2D.
En el parámetro offset,ISurfaceImageSourceNativeWithD2D::BeginDraw devuelve el desplazamiento de punto (un valor x,y) del rectángulo de destino actualizado. Puede usar ese desplazamiento para determinar dónde dibujar el contenido actualizado con ID2D1DeviceContext.
// MainPage.cpp | paste this at the end of MainPage::ClickHandler winrt::com_ptr<::IDXGISurface> dxgiSurface; RECT updateRect{ 0, 0, 500, 500 }; POINT offset{ 0, 0 }; HRESULT beginDrawHR = sisNativeWithD2D->BeginDraw( updateRect, __uuidof(::IDXGISurface), dxgiSurface.put_void(), &offset); // Create render target. winrt::com_ptr<::ID2D1Bitmap1> bitmap; winrt::check_hresult( d2dDeviceContext->CreateBitmapFromDxgiSurface( dxgiSurface.get(), nullptr, bitmap.put() ) ); // Set context's render target. d2dDeviceContext->SetTarget(bitmap.get());Use el contexto de dispositivo 2D directo para dibujar el contenido de SurfaceImageSource. Solo se dibuja el área especificada para la actualización en el paso anterior del parámetro updateRect.
// MainPage.cpp | paste this at the end of MainPage::ClickHandler if (beginDrawHR == DXGI_ERROR_DEVICE_REMOVED || beginDrawHR == DXGI_ERROR_DEVICE_RESET || beginDrawHR == D2DERR_RECREATE_TARGET) { // The Direct3D and Direct2D devices were lost and need to be re-created. // Recovery steps are: // 1) Re-create the Direct3D and Direct2D devices // 2) Call ISurfaceImageSourceNativeWithD2D::SetDevice with the new Direct2D // device // 3) Redraw the contents of the SurfaceImageSource } else if (beginDrawHR == E_SURFACE_CONTENTS_LOST) { // The devices were not lost but the entire contents of the surface // were. Recovery steps are: // 1) Call ISurfaceImageSourceNativeWithD2D::SetDevice with the Direct2D // device again // 2) Redraw the entire contents of the SurfaceImageSource } else { // Draw using Direct2D context. d2dDeviceContext->BeginDraw(); d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Orange)); winrt::com_ptr<::ID2D1SolidColorBrush> brush; winrt::check_hresult(d2dDeviceContext->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::Chocolate), D2D1::BrushProperties(0.8f), brush.put())); D2D1_SIZE_F const size{ 500, 500 }; D2D1_RECT_F const rect{ 100.0f, 100.0f, size.width - 100.0f, size.height - 100.0f }; d2dDeviceContext->DrawRectangle(rect, brush.get(), 100.0f); d2dDeviceContext->EndDraw(); }Llame a ISurfaceImageSourceNativeWithD2D::EndDraw para completar el mapa de bits (debe llamar a ISurfaceImageSourceNativeWithD2D::EndDraw solo desde el subproceso de la interfaz de usuario). A continuación, establezca SurfaceImageSource en una imagen XAML (o ImageBrush)para mostrarla en la interfaz de usuario XAML.
// MainPage.cpp | paste this at the end of MainPage::ClickHandler sisNativeWithD2D->EndDraw(); // The SurfaceImageSource object's underlying // ISurfaceImageSourceNativeWithD2D object will contain the completed bitmap. theImage().Source(surfaceImageSource);Nota
La llamada a SurfaceImageSource::SetSource (heredada de IBitmapSource::SetSource)produce actualmente una excepción. No lo llame desde el objeto SurfaceImageSource.
Por último, agregue el siguiente elemento Image dentro del marcado XAML existente en .
<!-- MainPage.xaml --> ... <Image x:Name="theImage" Width="500" Height="500" /> ...Ya puede compilar y ejecutar la aplicación. Haga clic en el botón para ver el contenido de SurfaceImageSource que se muestra en la imagen.

VirtualSurfaceImageSource
VirtualSurfaceImageSource extiende SurfaceImageSourcey es para escenarios en los que el contenido es potencialmente demasiado grande para caber en la pantalla a la vez (o demasiado grande para caber en la memoria de vídeo como una sola textura), por lo que el contenido debe virtualizarse para representarse de forma óptima. Por ejemplo, aplicaciones de asignación o lienzos de documentos grandes.
Sugerencia
La aplicación de ejemplo de invocación compleja muestra VirtualSurfaceImageSource.
VirtualSurfaceImageSource difiere de SurfaceImageSource en que usa una devolución de llamada(IVirtualSurfaceImageSourceCallbacksNative::UpdatesNeeded)que se implementa para actualizar las regiones de la superficie a medida que se vuelven visibles en la pantalla. No es necesario borrar las regiones ocultas, ya que el marco XAML se encarga de ello.
Es una buena idea familiarizarse con SurfaceImageSource (consulte la sección SurfaceImageSource anterior) antes de abordar VirtualSurfaceImageSource. Pero en un nivel muy alto, este es el proceso para crear y actualizar un virtualSurfaceImageSource.
- Implemente la interfaz IVirtualSurfaceImageSourceCallbackNative.
- Cree un dispositivo Direct 3D, un dispositivo Direct 2D y un contexto de dispositivo Direct 2D.
- Cree un origen VirtualSurfaceImageSourcey establezca el dispositivo Direct 2D (o Direct 3D) en ese dispositivo.
- Llame a RegisterForUpdatesNeeded enVirtualSurfaceImageSource.
- En la devolución de llamada UpdatesNeeded, llame a GetUpdateRectCount y GetUpdateRects.
- Represente los rectángulos de actualización (con BeginDrawEndDraw igual que para SurfaceImageSource).
- Establezca SurfaceImageSource en una imagen XAML oImageBrush para mostrarla en la interfaz de usuario xaml.
Y este es un análisis más profundo de esos pasos, con ejemplos de código fuente.
Puede seguir el código que se muestra y se describe a continuación mediante la creación de un nuevo proyecto en Microsoft Visual Studio. Cree un proyecto de aplicación en blanco (C++/WinRT) y asízguele el nombre VSISDemo (es importante dar este nombre al proyecto si va a pegar copias en las listas de código que se indican a continuación). Elija como destino la versión más reciente disponible de manera general (es decir, no en versión preliminar) de Windows SDK.
Abra
pch.hy agreguepch.hsiguiente, que incluye debajo de los que ya existen.// pch.h ... #include <d3d11_4.h> #include <d2d1_1.h> #include <windows.ui.xaml.media.dxinterop.h> #include <winrt/Windows.UI.Xaml.Media.Imaging.h>En este paso, proporcionará una implementación de la interfaz IVirtualSurfaceUpdatesCallbackNative. Agregue un nuevo elemento de archivo de encabezado (.h) al proyecto y asípómelo CallbackImplementation.h. Reemplace el contenido de ese archivo por la lista siguiente. El código se explica después de la lista.
#include "pch.h" namespace winrt::VSISDemo::implementation { struct CallbackImplementation : winrt::implements<CallbackImplementation, ::IVirtualSurfaceUpdatesCallbackNative> { CallbackImplementation( winrt::com_ptr<::ISurfaceImageSourceNativeWithD2D> sisNativeWithD2D, winrt::com_ptr<::IVirtualSurfaceImageSourceNative> const& vsisNative, winrt::com_ptr<::ID2D1DeviceContext> const& d2dDeviceContext) : m_sisNativeWithD2D(sisNativeWithD2D), m_vsisNative(vsisNative), m_d2dDeviceContext(d2dDeviceContext) {} IFACEMETHOD(UpdatesNeeded)() { HRESULT hr = S_OK; ULONG drawingBoundsCount = 0; m_vsisNative->GetUpdateRectCount(&drawingBoundsCount); std::unique_ptr<RECT[]> drawingBounds( new RECT[drawingBoundsCount]); m_vsisNative->GetUpdateRects( drawingBounds.get(), drawingBoundsCount); for (ULONG i = 0; i < drawingBoundsCount; ++i) { winrt::com_ptr<::IDXGISurface> dxgiSurface; POINT offset{ 0, 0 }; HRESULT beginDrawHR = m_sisNativeWithD2D->BeginDraw( drawingBounds[i], __uuidof(::IDXGISurface), dxgiSurface.put_void(), &offset); // Create render target. winrt::com_ptr<::ID2D1Bitmap1> bitmap; winrt::check_hresult( m_d2dDeviceContext->CreateBitmapFromDxgiSurface( dxgiSurface.get(), nullptr, bitmap.put() ) ); // Set context's render target. m_d2dDeviceContext->SetTarget(bitmap.get()); if (beginDrawHR == DXGI_ERROR_DEVICE_REMOVED || beginDrawHR == DXGI_ERROR_DEVICE_RESET || beginDrawHR == D2DERR_RECREATE_TARGET) { // The Direct3D and Direct2D devices were lost and need to be re-created. // Recovery steps are: // 1) Re-create the Direct3D and Direct2D devices // 2) Call ISurfaceImageSourceNativeWithD2D::SetDevice with the new Direct2D // device // 3) Redraw the contents of the SurfaceImageSource } else if (beginDrawHR == E_SURFACE_CONTENTS_LOST) { // The devices were not lost but the entire contents of the surface // were. Recovery steps are: // 1) Call ISurfaceImageSourceNativeWithD2D::SetDevice with the Direct2D // device again // 2) Redraw the entire contents of the SurfaceImageSource } else { // Draw using Direct2D context. m_d2dDeviceContext->BeginDraw(); m_d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Orange)); winrt::com_ptr<::ID2D1SolidColorBrush> brush; winrt::check_hresult(m_d2dDeviceContext->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::Chocolate), D2D1::BrushProperties(0.8f), brush.put())); D2D1_SIZE_F const size{ drawingBounds[i].right - drawingBounds[i].left, drawingBounds[i].bottom - drawingBounds[i].top }; D2D1_RECT_F const rect{ 100.0f, 100.0f, size.width - 100.0f, size.height - 100.0f }; m_d2dDeviceContext->DrawRectangle(rect, brush.get(), 100.0f); m_d2dDeviceContext->EndDraw(); } m_sisNativeWithD2D->EndDraw(); } return hr; } private: winrt::com_ptr<::ISurfaceImageSourceNativeWithD2D> m_sisNativeWithD2D{ nullptr }; winrt::com_ptr<::IVirtualSurfaceImageSourceNative> m_vsisNative{ nullptr }; winrt::com_ptr<::ID2D1DeviceContext> m_d2dDeviceContext{ nullptr }; }; }Siempre que sea necesario actualizar una región de VirtualSurfaceImageSource, el marco llama a la implementación de IVirtualSurfaceUpdatesCallbackNative::UpdatesNeeded (mostrado anteriormente).
Esto puede ocurrir cuando el marco determina que es necesario dibujar la región (cuando el usuario hace un desplazamiento panorámico o acerca la vista de la superficie, por ejemplo) o después de que la aplicación haya llamado a IVirtualSurfaceImageSourceNative::Invalidate en esa región.
En la implementación de IVirtualSurfaceImageSourceNative::UpdatesNeeded, use los métodos IVirtualSurfaceImageSourceNative::GetUpdateRectCount e IVirtualSurfaceImageSourceNative::GetUpdateRects para determinar qué regiones de la superficie se deben dibujar.
Para cada región que se debe actualizar, dibuje el contenido específico en esa región, pero limite el dibujo a las regiones limitadas para mejorar el rendimiento. Los detalles de llamar a los métodos ISurfaceImageSourceNativeWithD2D son los mismos que para SurfaceImageSource (consulte la sección SurfaceImageSource anterior).
Nota
Evite dibujar en VirtualSurfaceImageSource mientras la ventana está oculta o inactiva; de lo contrario, se producirá un error en las API ISurfaceImageSourceNativeWithD2D. Controle los eventos en torno a la visibilidad de la ventana y la suspensión de la aplicación para lograrlo.
En la clase MainPage, agregaremos un miembro de tipo CallbackImplementation. También crearemos un dispositivo Direct 3D, un dispositivo Direct 2D y un contexto de dispositivo Direct 2D. Para ello, llamaremos a D3D11CreateDevice,D2D1CreateDevicee ID2D1Device::CreateDeviceContext.
Reemplace el contenido de
MainPage.idl, y por el contenido de las listasMainPage.hMainPage.cppsiguientes.// MainPage.idl namespace VSISDemo { [default_interface] runtimeclass MainPage : Windows.UI.Xaml.Controls.Page { MainPage(); } }// MainPage.h #pragma once #include "MainPage.g.h" #include "CallbackImplementation.h" namespace winrt::VSISDemo::implementation { struct MainPage : MainPageT<MainPage> { MainPage(); void ClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); private: winrt::com_ptr<::IVirtualSurfaceUpdatesCallbackNative> m_cbi{ nullptr }; }; } namespace winrt::VSISDemo::factory_implementation { struct MainPage : MainPageT<MainPage, implementation::MainPage> { }; }// MainPage.cpp #include "pch.h" #include "MainPage.h" #include "MainPage.g.cpp" using namespace winrt; using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml::Media::Imaging; namespace winrt::VSISDemo::implementation { MainPage::MainPage() { InitializeComponent(); } void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&) { myButton().Content(box_value(L"Clicked")); uint32_t creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; 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 device. winrt::com_ptr<::ID3D11Device> d3dDevice; D3D_FEATURE_LEVEL supportedFeatureLevel; winrt::check_hresult(::D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, creationFlags, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, d3dDevice.put(), &supportedFeatureLevel, nullptr) ); // Get the Direct3D device. winrt::com_ptr<::IDXGIDevice> dxgiDevice{ d3dDevice.as<::IDXGIDevice>() }; // Create the Direct2D device and a corresponding context. winrt::com_ptr<::ID2D1Device> d2dDevice; ::D2D1CreateDevice(dxgiDevice.get(), nullptr, d2dDevice.put()); winrt::com_ptr<::ID2D1DeviceContext> d2dDeviceContext; winrt::check_hresult( d2dDevice->CreateDeviceContext( D2D1_DEVICE_CONTEXT_OPTIONS_NONE, d2dDeviceContext.put() ) ); } }A continuación, agregue código para crear un origen VirtualSurfaceImageSource con el tamaño que desee y establezca el dispositivo Direct 2D (o Direct 3D) en él mediante una llamada a ISurfaceImageSourceNativeWithD2D::SetDevice.
Nota
Si va a dibujar a VirtualSurfaceImageSource desde un subproceso en segundo plano, también deberá asegurarse de que el dispositivo DXGI tiene habilitado el acceso multiproceso (como se muestra en el código siguiente). Por motivos de rendimiento, solo debe hacerlo si va a dibujar desde un subproceso en segundo plano.
Para establecer el dispositivo y ejecutar las operaciones de dibujo, necesitamos un puntero a ISurfaceImageSourceNativeWithD2D. Para obtener uno, consulte el objeto VirtualSurfaceImageSource para su interfaz ISurfaceImageSourceNativeWithD2D subyacente.
Consulte también IVirtualSurfaceImageSourceNativey llame a IVirtualSurfaceImageSourceNative::RegisterForUpdatesNeeded,proporcionando la implementación de IVirtualSurfaceUpdatesCallbackNative.
A continuación, establezca SurfaceImageSource en una imagen XAML (o ImageBrush)para mostrarla en la interfaz de usuario XAML.
// MainPage.cpp | paste this at the end of MainPage::ClickHandler VirtualSurfaceImageSource virtualSurfaceImageSource(2000, 2000); winrt::com_ptr<::ISurfaceImageSourceNativeWithD2D> sisNativeWithD2D{ virtualSurfaceImageSource.as<::ISurfaceImageSourceNativeWithD2D>() }; // Associate the Direct2D device with the SurfaceImageSource. sisNativeWithD2D->SetDevice(d2dDevice.get()); // To enable multi-threaded access (optional) winrt::com_ptr<::ID3D11Multithread> d3dMultiThread{ d3dDevice.as<::ID3D11Multithread>() }; d3dMultiThread->SetMultithreadProtected(true); winrt::com_ptr<::IVirtualSurfaceImageSourceNative> vsisNative{ virtualSurfaceImageSource.as<::IVirtualSurfaceImageSourceNative>() }; m_cbi = winrt::make<CallbackImplementation>(sisNativeWithD2D, vsisNative, d2dDeviceContext); vsisNative->RegisterForUpdatesNeeded(m_cbi.as<::IVirtualSurfaceUpdatesCallbackNative>().get()); // The SurfaceImageSource object's underlying // ISurfaceImageSourceNativeWithD2D object will contain the completed bitmap. theImage().Source(virtualSurfaceImageSource);Por último, agregue el siguiente elemento Image dentro del marcado XAML existente en .
<!-- MainPage.xaml --> ... <Image x:Name="theImage" Width="500" Height="500" /> ...Ya puede compilar y ejecutar la aplicación. Haga clic en el botón para ver el contenido de VirtualSurfaceImageSource que se muestra en la imagen.
SwapChainPanel y juegos
SwapChainPanel es el tipo de Windows Runtime diseñado para elementos gráficos y juegos de alto rendimiento, en los que puedes administrar la cadena de intercambio directamente. En este caso, puedes crear tu propia cadena de intercambio de DirectX y administrar la presentación del contenido representado. Otra característica de SwapChainPanel es que puedes superponer otros elementos XAML delante de él.
Sugerencia
En las siguientes aplicaciones de ejemplo se muestra SurfaceImageSource:representación avanzada de imágenes de color de Direct2D,ajuste de foto de Direct2D,representación de imágenes SVG de Direct2D,entrada de baja latencia,juego de DirectXy XAML e interoperabilidad directX swapChainPanel de XAML (Windows 8.1).
Para garantizar un buen rendimiento, hay ciertas limitaciones en el tipo SwapChainPanel.
- No debería haber más de 4 instancias de SwapChainPanel por aplicación.
- Debe establecer el alto y el ancho de la cadena de intercambio de DirectX (en DXGI_SWAP_CHAIN_DESC1 )en las dimensiones actuales del elemento de cadena de intercambio. Si no lo hace, el contenido para mostrar se escalará para ajustarse (mediante DXGI_SCALING_STRETCH).
- Debe establecer el modo de escalado de la cadena de intercambio de DirectX (en DXGI_SWAP_CHAIN_DESC1) en DXGI_SCALING_STRETCH.
- Debes crear la cadena de intercambio de DirectX llamando a IDXGIFactory2::CreateSwapChainForComposition.
Actualice SwapChainPanel en función de las necesidades de la aplicación y no se sincronice con las actualizaciones del marco XAML. Si necesita sincronizar las actualizaciones de SwapChainPanel con las del marco XAML, regístrese para el evento Windows::UI::Xaml::Media::CompositionTarget::Rendering. En caso contrario, deberás tener en cuenta los posibles problemas entre subprocesos al intentar actualizar los elementos XAML de un subproceso distinto al que actualiza el objeto SwapChainPanel.
Si necesita recibir una entrada de puntero de baja latencia a SwapChainPanel,use SwapChainPanel::CreateCoreIndependentInputSource. Ese método devuelve un objeto CoreIndependentInputSource que se puede usar para recibir eventos de entrada con una latencia mínima en un subproceso en segundo plano. Tenga en cuenta que una vez que se llama a este método, no se generarán eventos de entrada de puntero XAML normales para SwapChainPanel, ya que todas las entradas se redirigirán al subproceso en segundo plano.
Este es el proceso para crear y actualizar un objeto SwapChainPanel.
Puede seguir el código que se muestra y se describe a continuación mediante la creación de un nuevo proyecto en Microsoft Visual Studio. Cree un proyecto de aplicación en blanco (C++/WinRT) y asízguele el nombre SCPDemo (es importante dar este nombre al proyecto si va a pegar copias en las listas de código que se indican a continuación). Elija como destino la versión más reciente disponible de manera general (es decir, no en versión preliminar) de Windows SDK.
Abra
pch.hy agreguepch.hsiguiente, que incluye debajo de los que ya existen.// pch.h ... #include <d3d11_4.h> #include <d2d1_1.h> #include <windows.ui.xaml.media.dxinterop.h>En la clase MainPage, primero crearemos un dispositivo Direct 3D, un dispositivo Direct 2D y un contexto de dispositivo Direct 2D. Para ello, llamaremos a D3D11CreateDevice,D2D1CreateDevicee ID2D1Device::CreateDeviceContext.
Reemplace el contenido de
MainPage.idl, y por el contenido de las listasMainPage.hMainPage.cppsiguientes.// MainPage.idl namespace SCPDemo { [default_interface] runtimeclass MainPage : Windows.UI.Xaml.Controls.Page { MainPage(); } }// MainPage.h #pragma once #include "MainPage.g.h" namespace winrt::SCPDemo::implementation { struct MainPage : MainPageT<MainPage> { MainPage(); void ClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); }; } namespace winrt::SCPDemo::factory_implementation { struct MainPage : MainPageT<MainPage, implementation::MainPage> { }; }// MainPage.cpp #include "pch.h" #include "MainPage.h" #include "MainPage.g.cpp" using namespace winrt; using namespace Windows::UI::Xaml; namespace winrt::SCPDemo::implementation { MainPage::MainPage() { InitializeComponent(); } void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&) { myButton().Content(box_value(L"Clicked")); uint32_t creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; 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 device. winrt::com_ptr<::ID3D11Device> d3dDevice; D3D_FEATURE_LEVEL supportedFeatureLevel; winrt::check_hresult(::D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, creationFlags, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, d3dDevice.put(), &supportedFeatureLevel, nullptr) ); // Get the Direct3D device. winrt::com_ptr<::IDXGIDevice> dxgiDevice{ d3dDevice.as<::IDXGIDevice>() }; // Create the Direct2D device and a corresponding context. winrt::com_ptr<::ID2D1Device> d2dDevice; ::D2D1CreateDevice(dxgiDevice.get(), nullptr, d2dDevice.put()); winrt::com_ptr<::ID2D1DeviceContext> d2dDeviceContext; winrt::check_hresult( d2dDevice->CreateDeviceContext( D2D1_DEVICE_CONTEXT_OPTIONS_NONE, d2dDeviceContext.put() ) ); } }Ajuste el marcado XAML en un elemento SwapChainPanel con . Los elementos XAML encapsulados se representarán delante de SwapChainPanel.
<!-- MainPage.xaml --> <SwapChainPanel x:Name="swapChainPanel"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center"> <Button x:Name="myButton" Click="ClickHandler">Click Me</Button> </StackPanel> </SwapChainPanel>A continuación, puede acceder a ese objeto SwapChainPanel a través de la función de accessor con el mismo nombre, como veremos.
A continuación, llame a IDXGIFactory2::CreateSwapChainForComposition para crear una cadena de intercambio.
// MainPage.cpp | paste this at the end of MainPage::ClickHandler // Get the DXGI adapter. winrt::com_ptr< ::IDXGIAdapter > dxgiAdapter; dxgiDevice->GetAdapter(dxgiAdapter.put()); // Get the DXGI factory. winrt::com_ptr< ::IDXGIFactory2 > dxgiFactory; dxgiFactory.capture(dxgiAdapter, &IDXGIAdapter::GetParent); DXGI_SWAP_CHAIN_DESC1 swapChainDesc { 0 }; swapChainDesc.Width = 500; swapChainDesc.Height = 500; swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swapchain format. swapChainDesc.Stereo = false; swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling. swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.BufferCount = 2; swapChainDesc.Scaling = DXGI_SCALING_STRETCH; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // We recommend using this swap effect for all applications. swapChainDesc.Flags = 0; // Create a swap chain by calling IDXGIFactory2::CreateSwapChainForComposition. winrt::com_ptr< ::IDXGISwapChain1 > swapChain; dxgiFactory->CreateSwapChainForComposition( d3dDevice.get(), &swapChainDesc, nullptr, swapChain.put());Obtenga un ISwapChainPanelNative desde swapChainPanel al que llamó swapChainPanel. Llamada a ISwapChainPanelNative::SetSwapChain para establecer la cadena de intercambio en SwapChainPanel.
// MainPage.cpp | paste this at the end of MainPage::ClickHandler // Get native interface for SwapChainPanel auto panelNative{ swapChainPanel().as<ISwapChainPanelNative>() }; winrt::check_hresult( panelNative->SetSwapChain(swapChain.get()) );Por último, dibuje en la cadena de intercambio de DirectX y, a continuación, presentarlo para mostrar el contenido.
// Create a Direct2D target bitmap associated with the // swap chain back buffer, and set it as the current target. D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1( D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED), 96.f, 96.f ); winrt::com_ptr<::IDXGISurface> dxgiBackBuffer; swapChain->GetBuffer(0, __uuidof(dxgiBackBuffer), dxgiBackBuffer.put_void()); winrt::com_ptr< ::ID2D1Bitmap1 > targetBitmap; winrt::check_hresult( d2dDeviceContext->CreateBitmapFromDxgiSurface( dxgiBackBuffer.get(), &bitmapProperties, targetBitmap.put() ) ); d2dDeviceContext->SetTarget(targetBitmap.get()); // Draw using Direct2D context. d2dDeviceContext->BeginDraw(); d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Orange)); winrt::com_ptr<::ID2D1SolidColorBrush> brush; winrt::check_hresult(d2dDeviceContext->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::Chocolate), D2D1::BrushProperties(0.8f), brush.put())); D2D1_SIZE_F const size{ 500, 500 }; D2D1_RECT_F const rect{ 100.0f, 100.0f, size.width - 100.0f, size.height - 100.0f }; d2dDeviceContext->DrawRectangle(rect, brush.get(), 100.0f); d2dDeviceContext->EndDraw(); swapChain->Present(1, 0);Los elementos XAML se actualizan cuando el diseño o la lógica de representación de Windows Runtime envían una señal de actualización.
Ya puede compilar y ejecutar la aplicación. Haga clic en el botón para ver el contenido de SwapChainPanel que se muestra detrás de los demás elementos XAML.

Nota
En general, la aplicación DirectX debe crear cadenas de intercambio en orientación horizontal e igual al tamaño de la ventana de presentación (que suele ser la resolución de pantalla nativa en la mayoría de Microsoft Store juegos). Esto garantiza que la aplicación usa la implementación óptima de la cadena de intercambio cuando no tiene ninguna superposición XAML visible. Si la aplicación gira al modo vertical, la aplicación debe llamar a IDXGISwapChain1::SetRotation en la cadena de intercambio existente, aplicar una transformación al contenido si es necesario y, a continuación, llamar a SetSwapChain de nuevo en la misma cadena de intercambio. De forma similar, la aplicación debe llamar de nuevo a SetSwapChain en la misma cadena de intercambio cada vez que se cambie el tamaño de la cadena de intercambio llamando a IDXGISwapChain::ResizeBuffers.