Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
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 de plateforme Windows universelle C++ (UWP) et afficher une couleur unie.
Conditions préalables
Nous partons du principe que vous connaissez C++. Vous avez également besoin d’une expérience de base avec les concepts de programmation graphique.
Temps pour terminer : 20 minutes.
Les 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 , de la Windows Runtime C++ Template Library (WRL), afin de pouvoir gérer la durée de vie de ces variables de manière sécurisée et à l'abri des exceptions. 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 parcourt le tableau dans son 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 Flags pour que les ressources Direct3D interagissent avec Direct2D. Si nous utilisons la version de débogage, nous passons également l’indicateur D3D11_CREATE_DEVICE_DEBUG. Pour plus d’informations sur le débogage d’applications, consultez Utilisation de la couche de débogage pour déboguer des applications.
Nous obtenons le périphérique Direct3D 11.1 (ID3D11Device1) et le contexte du périphérique (ID3D11DeviceContext1) en effectuant une requête sur le périphérique Direct3D 11 et le contexte de périphérique qui sont 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 nous avons défini le membre Format sur DXGI_FORMAT_B8G8R8A8_UNORM. Nous définissons le membre Count de la structure DXGI_SAMPLE_DESC à 1, tel que spécifié par le membre SampleDesc, et le membre Quality de DXGI_SAMPLE_DESC à zéro, car le flip-model ne prend pas en charge l'anticrénelage par échantillonnage multiple (MSAA). Nous définissons le membre BufferCount à 2 afin que le swap chain puisse utiliser une mémoire tampon frontale pour se 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 en tant que nombre maximal d’images tampons 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 nous appelons 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 arrière sous forme de texture 2D (ID3D11Texture2D). Pour créer la vue de la cible de rendu, nous appelons ID3D11Device::CreateRenderTargetView avec le tampon arrière de la chaîne d’échange. Nous devons spécifier de dessiner dans l’intégralité de la fenêtre principale 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 viewport dans un appel à ID3D11DeviceContext::RSSetViewports pour lier le viewport à l'étape de rasteriseur 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 entrons dans une boucle infinie pour rendre 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 réinitialiser la cible de rendu à une couleur unie.
- IDXGISwapChain::Present 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 laissons le nombre de mémoires tampons et le format des mémoires tampons inchangé (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