Configurer des ressources DirectX et afficher une image
Ici, nous vous montrons comment créer un appareil Direct3D, une chaîne d’échange et une vue cible de rendu, et comment présenter l’image rendue à l’affichage.
Objectif : Pour configurer des ressources DirectX dans une application plateforme Windows universelle C++ (UWP) et afficher une couleur unie.
Prérequis
Nous partons du principe que vous connaissez C++. Vous avez également besoin d’une expérience de base avec les concepts de programmation graphique.
Durée d’exécution : 20 minutes.
Instructions
1. Déclaration de variables d’interface Direct3D avec ComPtr
Nous déclarons des variables d’interface Direct3D avec le modèle de pointeur intelligent ComPtr à partir de la bibliothèque de modèles C++ Windows Runtime, afin de pouvoir gérer la durée de vie de ces variables de manière sécurisée. Nous pouvons ensuite utiliser ces variables pour accéder à la classe ComPtr et à ses membres. Par exemple :
ComPtr<ID3D11RenderTargetView> m_renderTargetView;
m_d3dDeviceContext->OMSetRenderTargets(
1,
m_renderTargetView.GetAddressOf(),
nullptr // Use no depth stencil.
);
Si vous déclarez ID3D11RenderTargetView avec ComPtr, vous pouvez ensuite utiliser la méthode GetAddressOf de ComPtr pour obtenir l’adresse du pointeur vers ID3D11RenderTargetView (**ID3D11RenderTargetView) pour passer à ID3D11DeviceContext ::OMSetRenderTargets. OMSetRenderTargets lie la cible de rendu à l’étape de fusion de sortie pour spécifier la cible de rendu comme cible de sortie.
Une fois l’exemple d’application démarré, il initialise et charge, puis est prêt à s’exécuter.
2. Création de l’appareil Direct3D
Pour utiliser l’API Direct3D pour afficher une scène, nous devons d’abord créer un appareil Direct3D qui représente l’adaptateur d’affichage. Pour créer l’appareil Direct3D, nous appelons la fonction D3D11CreateDevice. Nous spécifions les niveaux 9.1 à 11.1 dans le tableau de valeurs D3D_FEATURE_LEVEL. Direct3D guide le tableau dans l’ordre et retourne le niveau de fonctionnalité le plus élevé pris en charge. Par conséquent, pour obtenir le niveau de fonctionnalité le plus élevé disponible, nous listons les entrées de tableau D3D_FEATURE_LEVEL de la plus haute à la plus basse. Nous transmettons l’indicateur D3D11_CREATE_DEVICE_BGRA_SUPPORT au paramètre Indicateurs pour que les ressources Direct3D interagissent avec Direct2D. Si nous utilisons la build de débogage, nous passons également l’indicateur D3D11_CREATE_DEVICE_DEBUG . Pour plus d’informations sur le débogage des applications, consultez Utilisation de la couche de débogage pour déboguer des applications.
Nous obtenons l’appareil Direct3D 11.1 (ID3D11Device1) et le contexte de l’appareil (ID3D11DeviceContext1) en interrogeant l’appareil Direct3D 11 et le contexte d’appareil retournés par D3D11CreateDevice.
// First, create the Direct3D device.
// This flag is required in order to enable compatibility with Direct2D.
UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#if defined(_DEBUG)
// If the project is in a debug build, enable debugging via SDK Layers with this flag.
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
// This array defines the ordering of feature levels that D3D should attempt to create.
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_1
};
ComPtr<ID3D11Device> d3dDevice;
ComPtr<ID3D11DeviceContext> d3dDeviceContext;
DX::ThrowIfFailed(
D3D11CreateDevice(
nullptr, // Specify nullptr to use the default adapter.
D3D_DRIVER_TYPE_HARDWARE,
nullptr, // leave as nullptr if hardware is used
creationFlags, // optionally set debug and Direct2D compatibility flags
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION, // always set this to D3D11_SDK_VERSION
&d3dDevice,
nullptr,
&d3dDeviceContext
)
);
// Retrieve the Direct3D 11.1 interfaces.
DX::ThrowIfFailed(
d3dDevice.As(&m_d3dDevice)
);
DX::ThrowIfFailed(
d3dDeviceContext.As(&m_d3dDeviceContext)
);
3. Création de la chaîne d’échange
Ensuite, nous créons une chaîne d’échange que l’appareil utilise pour le rendu et l’affichage. Nous déclarons et initialisons une structure DXGI_SWAP_CHAIN_DESC1 pour décrire la chaîne d’échange. Ensuite, nous avons configuré la chaîne d’échange comme modèle de basculement (c’est-à-dire une chaîne d’échange dont la valeur DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL est définie dans le membre SwapEffect) et définissez le membre Format sur DXGI_FORMAT_B8G8R8A8_UNORM. Nous définissons le membre Count de la structure DXGI_SAMPLE_DESC que le membre SampleDesc spécifie sur 1 et le membre Qualité de DXGI_SAMPLE_DESC à zéro, car le modèle inverse ne prend pas en charge plusieurs exemples d’antialiasing (MSAA). Nous définissons le membre BufferCount sur 2 afin que la chaîne d’échange puisse utiliser une mémoire tampon frontale pour présenter à l’appareil d’affichage et à une mémoire tampon arrière qui sert de cible de rendu.
Nous obtenons l’appareil DXGI sous-jacent en interrogeant l’appareil Direct3D 11.1. Pour réduire la consommation d’alimentation, ce qui est important pour les appareils à batterie tels que les ordinateurs portables et les tablettes, nous appelons la méthode IDXGIDevice1 ::SetMaximumFrameLatency avec 1 comme nombre maximal d’images de mémoire tampon arrière que DXGI peut mettre en file d’attente. Cela garantit que l’application est rendue uniquement après le vide vertical.
Pour enfin créer la chaîne d’échange, nous devons obtenir la fabrique parente à partir de l’appareil DXGI. Nous appelons IDXGIDevice ::GetAdapter pour obtenir l’adaptateur de l’appareil, puis appelez IDXGIObject ::GetParent sur l’adaptateur pour obtenir la fabrique parente (IDXGIFactory2). Pour créer la chaîne d’échange, nous appelons IDXGIFactory2 ::CreateSwapChainForCoreWindow avec le descripteur de la chaîne d’échange et la fenêtre principale de l’application.
// If the swap chain does not exist, create it.
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
swapChainDesc.Stereo = false;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.Scaling = DXGI_SCALING_NONE;
swapChainDesc.Flags = 0;
// Use automatic sizing.
swapChainDesc.Width = 0;
swapChainDesc.Height = 0;
// This is the most common swap chain format.
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
// Don't use multi-sampling.
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
// Use two buffers to enable the flip effect.
swapChainDesc.BufferCount = 2;
// We recommend using this swap effect for all applications.
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
// Once the swap chain description is configured, it must be
// created on the same adapter as the existing D3D Device.
// First, retrieve the underlying DXGI Device from the D3D Device.
ComPtr<IDXGIDevice2> dxgiDevice;
DX::ThrowIfFailed(
m_d3dDevice.As(&dxgiDevice)
);
// Ensure that DXGI does not queue more than one frame at a time. This both reduces
// latency and ensures that the application will only render after each VSync, minimizing
// power consumption.
DX::ThrowIfFailed(
dxgiDevice->SetMaximumFrameLatency(1)
);
// Next, get the parent factory from the DXGI Device.
ComPtr<IDXGIAdapter> dxgiAdapter;
DX::ThrowIfFailed(
dxgiDevice->GetAdapter(&dxgiAdapter)
);
ComPtr<IDXGIFactory2> dxgiFactory;
DX::ThrowIfFailed(
dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
);
// Finally, create the swap chain.
CoreWindow^ window = m_window.Get();
DX::ThrowIfFailed(
dxgiFactory->CreateSwapChainForCoreWindow(
m_d3dDevice.Get(),
reinterpret_cast<IUnknown*>(window),
&swapChainDesc,
nullptr, // Allow on all displays.
&m_swapChain
)
);
4. Création de la vue de la cible de rendu
Pour afficher des graphiques dans la fenêtre, nous devons créer une vue de cible de rendu. Nous appelons IDXGISwapChain ::GetBuffer pour obtenir la mémoire tampon de retour de la chaîne d’échange à utiliser lorsque nous créons la vue de la cible de rendu. Nous spécifions la mémoire tampon back en tant que texture 2D (ID3D11Texture2D). Pour créer la vue de la cible de rendu, nous appelons ID3D11Device ::CreateRenderTargetView avec la mémoire tampon back de la chaîne d’échange. Nous devons spécifier de dessiner dans la fenêtre principale entière en spécifiant le port d’affichage (D3D11_VIEWPORT) comme taille totale de la mémoire tampon de retour de la chaîne d’échange. Nous utilisons le port d’affichage dans un appel à ID3D11DeviceContext ::RSSetViewports pour lier le port d’affichage à l’étape de rastériseur du pipeline. L’étape de rastériseur convertit les informations vectorielles en image raster. Dans ce cas, nous n’avons pas besoin d’une conversion, car nous affichons simplement une couleur unie.
// Once the swap chain is created, create a render target view. This will
// allow Direct3D to render graphics to the window.
ComPtr<ID3D11Texture2D> backBuffer;
DX::ThrowIfFailed(
m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
);
DX::ThrowIfFailed(
m_d3dDevice->CreateRenderTargetView(
backBuffer.Get(),
nullptr,
&m_renderTargetView
)
);
// After the render target view is created, specify that the viewport,
// which describes what portion of the window to draw to, should cover
// the entire window.
D3D11_TEXTURE2D_DESC backBufferDesc = {0};
backBuffer->GetDesc(&backBufferDesc);
D3D11_VIEWPORT viewport;
viewport.TopLeftX = 0.0f;
viewport.TopLeftY = 0.0f;
viewport.Width = static_cast<float>(backBufferDesc.Width);
viewport.Height = static_cast<float>(backBufferDesc.Height);
viewport.MinDepth = D3D11_MIN_DEPTH;
viewport.MaxDepth = D3D11_MAX_DEPTH;
m_d3dDeviceContext->RSSetViewports(1, &viewport);
5. Présentation de l’image rendue
Nous entrerons une boucle infinie pour afficher et afficher continuellement la scène.
Dans cette boucle, nous appelons :
- ID3D11DeviceContext ::OMSetRenderTargets pour spécifier la cible de rendu comme cible de sortie.
- ID3D11DeviceContext ::ClearRenderTargetView pour effacer la cible de rendu en couleur unie.
- IDXGISwapChain ::P resent pour présenter l’image rendue à la fenêtre.
Étant donné que nous définissions précédemment la latence maximale d’images sur 1, Windows ralentit généralement la boucle de rendu vers la fréquence d’actualisation de l’écran, généralement autour de 60 Hz. Windows ralentit la boucle de rendu en mettant l’application en veille lorsque l’application appelle Present. Windows met l’application en veille jusqu’à ce que l’écran soit actualisé.
// Enter the render loop. Note that UWP apps should never exit.
while (true)
{
// Process events incoming to the window.
m_window->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
// Specify the render target we created as the output target.
m_d3dDeviceContext->OMSetRenderTargets(
1,
m_renderTargetView.GetAddressOf(),
nullptr // Use no depth stencil.
);
// Clear the render target to a solid color.
const float clearColor[4] = { 0.071f, 0.04f, 0.561f, 1.0f };
m_d3dDeviceContext->ClearRenderTargetView(
m_renderTargetView.Get(),
clearColor
);
// Present the rendered image to the window. Because the maximum frame latency is set to 1,
// the render loop will generally be throttled to the screen refresh rate, typically around
// 60 Hz, by sleeping the application on Present until the screen is refreshed.
DX::ThrowIfFailed(
m_swapChain->Present(1, 0)
);
}
6. Redimensionnement de la fenêtre de l’application et de la mémoire tampon de la chaîne d’échange
Si la taille de la fenêtre de l’application change, l’application doit redimensionner les mémoires tampons de la chaîne d’échange, recréer la vue cible de rendu, puis présenter l’image rendue redimensionnée. Pour redimensionner les mémoires tampons de la chaîne d’échange, nous appelons IDXGISwapChain ::ResizeBuffers. Dans cet appel, nous laissez le nombre de mémoires tampons et le format des mémoires tampons inchangées (le paramètre BufferCount à deux et le paramètre NewFormat à DXGI_FORMAT_B8G8R8A8_UNORM). Nous rendons la taille de la mémoire tampon de retour de la chaîne d’échange de la même taille que la fenêtre redimensionnée. Après avoir redimensionné les mémoires tampons de la chaîne d’échange, nous créons la nouvelle cible de rendu et présentons la nouvelle image rendue de la même façon que lorsque nous avons initialisé l’application.
// If the swap chain already exists, resize it.
DX::ThrowIfFailed(
m_swapChain->ResizeBuffers(
2,
0,
0,
DXGI_FORMAT_B8G8R8A8_UNORM,
0
)
);
Résumé et étapes suivantes
Nous avons créé un appareil Direct3D, une chaîne d’échange et une vue cible de rendu, et présenté l’image rendue à l’affichage.
Ensuite, nous dessinons également un triangle sur l’écran.
Création de nuanceurs et de primitives de dessin