Gambaran umum interoperabilitas Direct2D dan Direct3D
Grafik 2-D dan 3-D yang dipercepat perangkat keras semakin menjadi bagian dari aplikasi non-game, dan sebagian besar aplikasi game menggunakan grafis 2-D dalam bentuk menu dan Heads-Up Displays (HUD). Ada banyak nilai yang dapat ditambahkan dengan memungkinkan penyajian 2-D tradisional dicampur dengan penyajian Direct3D dengan cara yang efisien.
Topik ini menjelaskan cara mengintegrasikan grafik 2-D dan 3-D dengan menggunakan Direct2D dan Direct3D.
Prasyarat
Gambaran umum ini mengasumsikan bahwa Anda terbiasa dengan operasi gambar Direct2D dasar. Untuk tutorial, lihat Membuat aplikasi Direct2D sederhana. Ini juga mengasumsikan bahwa Anda dapat memprogram dengan menggunakan Direct3D 10.1.
Versi Direct3D yang didukung
Dengan DirectX 11.0, Direct2D hanya mendukung interoperabilitas dengan perangkat Direct3D 10.1. Dengan DirectX 11.1 atau yang lebih baru, Direct2D juga mendukung interoperabilitas dengan Direct3D 11.
Interoperabilitas melalui DXGI
Pada Direct3D 10, runtime Direct3D menggunakan DXGI untuk manajemen sumber daya. Lapisan runtime DXGI menyediakan berbagi lintas proses permukaan memori video dan berfungsi sebagai fondasi untuk platform runtime berbasis memori video lainnya. Direct2D menggunakan DXGI untuk beroperasi dengan Direct3D.
Ada dua cara utama untuk menggunakan Direct2D dan Direct3D bersama-sama:
- Anda dapat menulis konten Direct2D ke permukaan Direct3D dengan mendapatkan IDXGISurface dan menggunakannya dengan CreateDxgiSurfaceRenderTarget untuk membuat ID2D1RenderTarget. Anda kemudian dapat menggunakan target render untuk menambahkan antarmuka dua dimensi atau latar belakang ke grafik tiga dimensi, atau menggunakan gambar Direct2D sebagai tekstur untuk objek tiga dimensi.
- Dengan menggunakan CreateSharedBitmap untuk membuat ID2D1Bitmap dari IDXGISurface, Anda dapat menulis adegan Direct3D ke bitmap dan merendernya dengan Direct2D.
Menulis ke permukaan Direct3D dengan target render permukaan DXGI
Untuk menulis ke permukaan Direct3D, Anda mendapatkan IDXGISurface dan meneruskannya ke metode CreateDxgiSurfaceRenderTarget untuk membuat target render permukaan DXGI. Anda kemudian dapat menggunakan target render permukaan DXGI untuk menggambar konten 2-D ke permukaan DXGI.
Target render permukaan DXGI adalah semacam ID2D1RenderTarget. Seperti target render Direct2D lainnya, Anda dapat menggunakannya untuk membuat sumber daya dan mengeluarkan perintah menggambar.
Target render permukaan DXGI dan permukaan DXGI harus menggunakan format DXGI yang sama. Jika Anda menentukan format DXGI_FORMAT_UNKOWN saat membuat target render, format permukaan akan otomatis digunakan.
Target render permukaan DXGI tidak melakukan sinkronisasi permukaan DXGI.
Membuat permukaan DXGI
Dengan Direct3D 10, ada beberapa cara untuk mendapatkan permukaan DXGI. Anda dapat membuat IDXGISwapChain untuk perangkat, lalu menggunakan metode GetBuffer rantai pertukaran untuk mendapatkan permukaan DXGI. Atau, Anda dapat menggunakan perangkat untuk membuat tekstur, lalu menggunakan tekstur tersebut sebagai permukaan DXGI.
Terlepas dari cara Anda membuat permukaan DXGI, permukaan harus menggunakan salah satu format DXGI yang didukung oleh target render permukaan DXGI. Untuk daftar, lihat Format Piksel dan Mode Alfa yang Didukung.
Selain itu, ID3D10Device1 yang terkait dengan permukaan DXGI harus mendukung format BGRA DXGI agar permukaan berfungsi dengan Direct2D. Untuk memastikan dukungan ini, gunakan bendera D3D10_CREATE_DEVICE_BGRA_SUPPORT saat Anda memanggil metode D3D10CreateDevice1 untuk membuat perangkat.
Kode berikut mendefinisikan metode yang membuat ID3D10Device1. Ini memilih tingkat fitur terbaik yang tersedia dan kembali ke Windows Advanced Rasterization Platform (WARP) ketika penyajian perangkat keras tidak tersedia.
HRESULT DXGISampleApp::CreateD3DDevice(
IDXGIAdapter *pAdapter,
D3D10_DRIVER_TYPE driverType,
UINT flags,
ID3D10Device1 **ppDevice
)
{
HRESULT hr = S_OK;
static const D3D10_FEATURE_LEVEL1 levelAttempts[] =
{
D3D10_FEATURE_LEVEL_10_0,
D3D10_FEATURE_LEVEL_9_3,
D3D10_FEATURE_LEVEL_9_2,
D3D10_FEATURE_LEVEL_9_1,
};
for (UINT level = 0; level < ARRAYSIZE(levelAttempts); level++)
{
ID3D10Device1 *pDevice = NULL;
hr = D3D10CreateDevice1(
pAdapter,
driverType,
NULL,
flags,
levelAttempts[level],
D3D10_1_SDK_VERSION,
&pDevice
);
if (SUCCEEDED(hr))
{
// transfer reference
*ppDevice = pDevice;
pDevice = NULL;
break;
}
}
return hr;
}
Contoh kode berikutnya menggunakan metode CreateD3DDevice yang ditunjukkan dalam contoh sebelumnya untuk membuat perangkat Direct3D yang dapat membuat permukaan DXGI untuk digunakan dengan Direct2D.
// Create device
hr = CreateD3DDevice(
NULL,
D3D10_DRIVER_TYPE_HARDWARE,
nDeviceFlags,
&pDevice
);
if (FAILED(hr))
{
hr = CreateD3DDevice(
NULL,
D3D10_DRIVER_TYPE_WARP,
nDeviceFlags,
&pDevice
);
}
Menulis konten Direct2D ke buffer rantai pertukaran
Cara paling sederhana untuk menambahkan konten Direct2D ke adegan Direct3D adalah dengan menggunakan metode GetBuffer dari IDXGISwapChain untuk mendapatkan permukaan DXGI, lalu menggunakan permukaan dengan metode CreateDxgiSurfaceRenderTarget untuk membuat ID2D1RenderTarget untuk menggambar konten 2-D Anda.
Pendekatan ini tidak merender konten Anda dalam tiga dimensi; tidak akan memiliki perspektif atau kedalaman. Namun, ini berguna untuk beberapa tugas umum:
- Membuat latar belakang 2-D untuk adegan 3-D.
- Membuat antarmuka 2-D di depan adegan 3-D.
- Menggunakan multisampling Direct3D saat merender konten Direct2D.
Bagian berikutnya menunjukkan cara membuat latar belakang 2-D untuk adegan 3-D.
Contoh: Menggambar latar belakang 2-D
Langkah-langkah berikut menjelaskan cara membuat target render permukaan DXGI dan menggunakannya untuk menggambar latar belakang gradien.
Gunakan metode CreateSwapChain untuk membuat rantai pertukaran untuk ID3D10Device1 (variabel m_pDevice ). Rantai pertukaran menggunakan format DXGI_FORMAT_B8G8R8A8_UNORM DXGI, salah satu format DXGI yang didukung oleh Direct2D.
if (SUCCEEDED(hr)) { hr = pDevice->QueryInterface(&m_pDevice); } if (SUCCEEDED(hr)) { hr = pDevice->QueryInterface(&pDXGIDevice); } if (SUCCEEDED(hr)) { hr = pDXGIDevice->GetAdapter(&pAdapter); } if (SUCCEEDED(hr)) { hr = pAdapter->GetParent(IID_PPV_ARGS(&pDXGIFactory)); } if (SUCCEEDED(hr)) { DXGI_SWAP_CHAIN_DESC swapDesc; ::ZeroMemory(&swapDesc, sizeof(swapDesc)); swapDesc.BufferDesc.Width = nWidth; swapDesc.BufferDesc.Height = nHeight; swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; swapDesc.BufferDesc.RefreshRate.Numerator = 60; swapDesc.BufferDesc.RefreshRate.Denominator = 1; swapDesc.SampleDesc.Count = 1; swapDesc.SampleDesc.Quality = 0; swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapDesc.BufferCount = 1; swapDesc.OutputWindow = m_hwnd; swapDesc.Windowed = TRUE; hr = pDXGIFactory->CreateSwapChain(m_pDevice, &swapDesc, &m_pSwapChain); }
Gunakan metode GetBuffer rantai pertukaran untuk mendapatkan permukaan DXGI.
// Get a surface in the swap chain hr = m_pSwapChain->GetBuffer( 0, IID_PPV_ARGS(&pBackBuffer) );
Gunakan permukaan DXGI untuk membuat target render DXGI.
// Initialize *hwnd* with the handle of the window displaying the rendered content. HWND hwnd; // Create the DXGI Surface Render Target. float dpi = GetDpiForWindow(hwnd); D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), dpiX, dpiY); // Create a Direct2D render target that can draw into the surface in the swap chain hr = m_pD2DFactory->CreateDxgiSurfaceRenderTarget( pBackBuffer, &props, &m_pBackBufferRT);
Gunakan target render untuk menggambar latar belakang gradien.
// Swap chain will tell us how big the back buffer is DXGI_SWAP_CHAIN_DESC swapDesc; hr = m_pSwapChain->GetDesc(&swapDesc); if (SUCCEEDED(hr)) { // Draw a gradient background. if (m_pBackBufferRT) { D2D1_SIZE_F targetSize = m_pBackBufferRT->GetSize(); m_pBackBufferRT->BeginDraw(); m_pBackBufferGradientBrush->SetTransform( D2D1::Matrix3x2F::Scale(targetSize) ); D2D1_RECT_F rect = D2D1::RectF( 0.0f, 0.0f, targetSize.width, targetSize.height ); m_pBackBufferRT->FillRectangle(&rect, m_pBackBufferGradientBrush); hr = m_pBackBufferRT->EndDraw(); } ...
Kode dihilangkan dari sampel ini.
Menggunakan konten Direct2D sebagai tekstur
Cara lain untuk menggunakan konten Direct2D dengan Direct3D adalah dengan menggunakan Direct2D untuk menghasilkan tekstur 2-D lalu menerapkan tekstur tersebut ke model 3-D. Anda melakukan ini dengan membuat ID3D10Texture2D, mendapatkan permukaan DXGI dari tekstur, lalu menggunakan permukaan untuk membuat target render permukaan DXGI. Permukaan ID3D10Texture2D harus menggunakan bendera ikat D3D10_BIND_RENDER_TARGET dan menggunakan format DXGI yang didukung oleh target render permukaan DXGI. Untuk daftar format DXGI yang didukung, lihat Format Piksel dan Mode Alfa yang Didukung.
Contoh: Menggunakan konten Direct2D sebagai tekstur
Contoh berikut menunjukkan cara membuat target render permukaan DXGI yang dirender ke tekstur 2-D (diwakili oleh ID3D10Texture2D).
Pertama, gunakan perangkat Direct3D untuk membuat tekstur 2-D. Tekstur menggunakan bendera ikat D3D10_BIND_RENDER_TARGET dan D3D10_BIND_SHADER_RESOURCE , dan menggunakan format DXGI DXGI_FORMAT_B8G8R8A8_UNORM , salah satu format DXGI yang didukung oleh Direct2D.
// Allocate an offscreen D3D surface for D2D to render our 2D content into D3D10_TEXTURE2D_DESC texDesc; texDesc.ArraySize = 1; texDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; texDesc.CPUAccessFlags = 0; texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; texDesc.Height = 512; texDesc.Width = 512; texDesc.MipLevels = 1; texDesc.MiscFlags = 0; texDesc.SampleDesc.Count = 1; texDesc.SampleDesc.Quality = 0; texDesc.Usage = D3D10_USAGE_DEFAULT; hr = m_pDevice->CreateTexture2D(&texDesc, NULL, &m_pOffscreenTexture);
Gunakan tekstur untuk mendapatkan permukaan DXGI.
IDXGISurface *pDxgiSurface = NULL; hr = m_pOffscreenTexture->QueryInterface(&pDxgiSurface);
Gunakan permukaan dengan metode CreateDxgiSurfaceRenderTarget untuk mendapatkan target render Direct2D.
if (SUCCEEDED(hr)) { // Create a D2D render target that can draw into our offscreen D3D // surface. Given that we use a constant size for the texture, we // fix the DPI at 96. D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), 96, 96); hr = m_pD2DFactory->CreateDxgiSurfaceRenderTarget( pDxgiSurface, &props, &m_pRenderTarget); }
Sekarang setelah Anda mendapatkan target render Direct2D dan mengaitkannya dengan tekstur Direct3D, Anda dapat menggunakan target render untuk menggambar konten Direct2D ke tekstur tersebut, dan Anda dapat menerapkan tekstur tersebut ke primitif Direct3D.
Kode dihilangkan dari sampel ini.
Mengubah ukuran target render permukaan DXGI
Target render permukaan DXGI tidak mendukung metode ID2D1RenderTarget::Resize . Untuk mengubah ukuran target render permukaan DXGI, aplikasi harus merilis dan membuatnya kembali.
Operasi ini berpotensi membuat masalah performa. Target render mungkin merupakan sumber daya Direct2D aktif terakhir yang menyimpan referensi ke ID3D10Device1 yang terkait dengan permukaan DXGI target render. Jika aplikasi merilis target render dan referensi ID3D10Device1 dihancurkan, yang baru harus dibuat ulang.
Anda dapat menghindari operasi yang berpotensi mahal ini dengan menyimpan setidaknya satu sumber daya Direct2D yang dibuat oleh target render saat Anda membuat ulang target render tersebut. Berikut ini adalah beberapa sumber daya Direct2D yang berfungsi untuk pendekatan ini:
- ID2D1Bitmap (yang mungkin ditahan secara tidak langsung oleh ID2D1BitmapBrush)
- ID2D1Layer
- ID2D1Mesh
Untuk mengakomodasi pendekatan ini, metode mengubah ukuran Anda harus menguji untuk melihat apakah perangkat Direct3D tersedia. Jika tersedia, lepaskan dan buat ulang target render permukaan DXGI Anda, tetapi simpan semua sumber daya yang mereka buat sebelumnya dan gunakan kembali. Ini berfungsi karena, seperti yang dijelaskan dalam Gambaran Umum Sumber Daya, sumber daya yang dibuat oleh dua target render kompatibel ketika kedua target render dikaitkan dengan perangkat Direct3D yang sama.