Kueri (Direct3D 9)
Ada beberapa jenis kueri yang dirancang untuk mengkueri status sumber daya. Status sumber daya tertentu mencakup status unit pemrosesan grafis (GPU), status driver, atau status runtime. Untuk memahami perbedaan antara berbagai jenis kueri, Anda perlu memahami status kueri. Diagram transisi status berikut menjelaskan setiap status kueri.
Diagram menunjukkan tiga status, masing-masing ditentukan oleh lingkaran. Masing-masing garis padat adalah peristiwa berbasis aplikasi yang menyebabkan transisi status. Garis putus-putus adalah peristiwa berbasis sumber daya yang mengalihkan kueri dari status yang dikeluarkan ke status yang disinyalir. Masing-masing negara bagian ini memiliki tujuan yang berbeda:
- Status yang disinyalir seperti status menganggur. Objek kueri telah dibuat dan sedang menunggu aplikasi mengeluarkan kueri. Setelah kueri selesai dan ditransisikan kembali ke status bersinyali, jawaban atas kueri dapat diambil.
- Status bangunan seperti area penahapan untuk kueri. Dari status bangunan, kueri telah dikeluarkan (dengan memanggil D3DISSUE_BEGIN) tetapi belum beralih ke status yang dikeluarkan. Saat aplikasi mengeluarkan akhir kueri (dengan memanggil D3DISSUE_END), kueri beralih ke status yang dikeluarkan.
- Status yang dikeluarkan berarti bahwa sumber daya yang dikueri memiliki kontrol kueri. Setelah sumber daya selesai bekerja, sumber daya akan mentransisikan komputer status ke status yang disinyalir. Selama status yang dikeluarkan, aplikasi harus melakukan polling untuk mendeteksi transisi ke status yang disinyalir. Setelah transisi ke status sinyal terjadi, GetData mengembalikan hasil kueri (melalui argumen) ke aplikasi.
Tabel berikut ini mencantumkan jenis kueri yang tersedia.
Beberapa kueri memerlukan peristiwa awal dan akhir, sementara yang lain hanya memerlukan peristiwa akhir. Kueri yang hanya memerlukan peristiwa akhir dimulai ketika peristiwa implisit lain terjadi (yang tercantum dalam tabel). Semua kueri mengembalikan jawaban, kecuali kueri peristiwa yang jawabannya selalu TRUE. Aplikasi menggunakan status kueri atau kode pengembalian GetData.
Sebelum membuat kueri, Anda bisa memeriksa untuk melihat apakah runtime mendukung kueri dengan memanggil CreateQuery dengan penunjuk NULL seperti ini:
IDirect3DQuery9* pEventQuery;
// Create a device pointer m_pd3dDevice
// Create a query object
HRESULT hr = m_pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, NULL);
Metode ini mengembalikan kode keberhasilan jika kueri dapat dibuat; jika tidak, kode kesalahan akan dikembalikan. Setelah CreateQuery berhasil, Anda bisa membuat objek kueri seperti ini:
IDirect3DQuery9* pEventQuery;
m_pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);
Jika panggilan ini berhasil, objek kueri dibuat. Kueri pada dasarnya menganggur dalam keadaan sinyal (dengan jawaban yang tidak diinisialisasi) menunggu untuk dikeluarkan. Setelah Anda selesai dengan kueri, rilis kueri seperti antarmuka lainnya.
Aplikasi mengubah status kueri dengan mengeluarkan kueri. Berikut adalah contoh penerbitan kueri:
IDirect3DQuery9* pEventQuery;
m_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);
// Issue a Begin event
pEventQuery->Issue(D3DISSUE_BEGIN);
or
// Issue an End event
pEventQuery->Issue(D3DISSUE_END);
Kueri dalam status tersinyal akan bertransisi seperti ini saat dikeluarkan:
Jenis Masalah | Transisi Kueri ke . . . |
---|---|
D3DISSUE_BEGIN | Membangun negara bagian. |
D3DISSUE_END | Status yang dikeluarkan. |
Kueri dalam status bangunan akan bertransisi seperti ini saat dikeluarkan:
Jenis Masalah | Transisi Kueri ke . . . |
---|---|
D3DISSUE_BEGIN | (Tidak ada transisi, tetap dalam keadaan bangunan. Memulai ulang tanda kurung kueri.) |
D3DISSUE_END | Status yang dikeluarkan. |
Kueri dalam status yang dikeluarkan akan bertransisi seperti ini saat dikeluarkan:
Jenis Masalah | Transisi Kueri ke . . . |
---|---|
D3DISSUE_BEGIN | Membangun status dan menghidupkan ulang kurung kueri. |
D3DISSUE_END | Status yang dikeluarkan setelah mengabaikan kueri yang ada. |
GetData melakukan dua hal:
- Mengembalikan status kueri dalam kode pengembalian.
- Mengembalikan jawaban untuk kueri di pData.
Dari masing-masing dari tiga status kueri, berikut adalah kode pengembalian GetData :
Status Kueri | Kode pengembalian GetData |
---|---|
Ditandai | S_OK |
Gedung | Kode kesalahan |
Dikeluarkan | S_FALSE |
Misalnya, saat kueri dalam status dikeluarkan dan jawaban atas kueri tidak tersedia, GetData mengembalikan S_FALSE. Ketika sumber daya menyelesaikan pekerjaannya dan aplikasi telah mengeluarkan akhir kueri, sumber daya akan mentransisikan kueri ke status yang disinyalkan. Dari status yang disinyalir, GetData mengembalikan S_OK yang berarti bahwa jawaban atas kueri juga dikembalikan dalam pData. Misalnya, berikut adalah urutan peristiwa untuk mengembalikan jumlah piksel (atau sampel saat multisampling diaktifkan) yang digambar dalam urutan render:
- Buat kueri.
- Terbitkan peristiwa mulai.
- Gambar sesuatu.
- Terbitkan peristiwa akhir.
Berikut ini adalah urutan kode yang sesuai:
IDirect3DQuery9* pOcclusionQuery;
DWORD numberOfSamplesDrawn;
m_pD3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery);
// Add an end marker to the command buffer queue.
pOcclusionQuery->Issue(D3DISSUE_BEGIN);
// API render loop
...
Draw(...)
...
// Add an end marker to the command buffer queue.
pOcclusionQuery->Issue(D3DISSUE_END);
// Force the driver to execute the commands from the command buffer.
// Empty the command buffer and wait until the GPU is idle.
while(S_FALSE == pOcclusionQuery->GetData( &numberOfSamplesDrawn,
sizeof(DWORD), D3DGETDATA_FLUSH ))
;
// To get the number of pixels drawn when multisampling is enabled,
// divide numberOfSamplesDrawn by the sample count of the render target.
Baris kode ini melakukan beberapa hal:
- Panggil GetData untuk mengembalikan jumlah piksel/sampel yang digambar.
- Tentukan D3DGETDATA_FLUSH untuk mengaktifkan sumber daya untuk transisi kueri ke status tersinyal.
- Polling sumber daya kueri dengan memanggil GetData dari perulangan. Selama GetData mengembalikan S_FALSE, ini berarti sumber daya belum mengembalikan jawabannya.
Nilai yang dikembalikan dari GetData pada dasarnya memberi tahu Anda dalam status kueri. Nilai yang mungkin S_OK, S_FALSE, dan kesalahan. Jangan panggil GetData pada kueri yang berada dalam status bangunan.
- S_OK berarti sumber daya (GPU atau driver, atau runtime) selesai. Kueri kembali ke status tersinyal. Jawabannya (jika ada) dikembalikan oleh GetData.
- S_FALSE berarti sumber daya (GPU atau driver, atau runtime) belum dapat mengembalikan jawaban. Ini bisa jadi karena GPU belum selesai atau belum melihat pekerjaan.
- Kesalahan berarti bahwa kueri telah menghasilkan kesalahan yang tidak dapat dipulihkan. Ini bisa jadi terjadi jika perangkat hilang selama kueri. Setelah kueri menghasilkan kesalahan (selain S_FALSE), kueri harus dibuat ulang yang akan memulai ulang urutan kueri dari status yang disinyalkan.
Alih-alih menentukan D3DGETDATA_FLUSH, yang menyediakan lebih banyak informasi terbaru, Anda dapat menyediakan nol yang merupakan pemeriksaan yang lebih ringan jika kueri dalam status dikeluarkan. Memasok nol akan menyebabkan GetData tidak membersihkan buffer perintah. Untuk alasan ini, perawatan harus dilakukan untuk menghindari perulangan tak terbatas (lihat GetData untuk detailnya). Karena runtime bekerja dalam buffer perintah, D3DGETDATA_FLUSH adalah mekanisme untuk membersihkan buffer perintah ke driver (dan karenanya GPU; lihat Membuat Profil Panggilan API Direct3D Secara Akurat (Direct3D 9)). Selama buffer perintah memerah, kueri dapat beralih ke status yang disinyalkan.
Kueri peristiwa tidak mendukung peristiwa awal.
- Buat kueri.
- Terbitkan peristiwa akhir.
- Polling hingga GPU menganggur.
- Terbitkan peristiwa akhir.
IDirect3DQuery9* pEventQuery = NULL;
m_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);
// Add an end marker to the command buffer queue.
pEventQuery->Issue(D3DISSUE_END);
// Empty the command buffer and wait until the GPU is idle.
while(S_FALSE == pEventQuery->GetData( NULL, 0, D3DGETDATA_FLUSH ))
;
... // API calls
// Add an end marker to the command buffer queue.
pEventQuery->Issue(D3DISSUE_END);
// Force the driver to execute the commands from the command buffer.
// Empty the command buffer and wait until the GPU is idle.
while(S_FALSE == pEventQuery->GetData( NULL, 0, D3DGETDATA_FLUSH ))
;
Ini adalah urutan perintah yang digunakan kueri peristiwa untuk memprofilkan panggilan antarmuka pemrograman aplikasi (API) (lihat Memprofilkan Panggilan API Direct3D Secara Akurat (Direct3D 9)). Urutan ini menggunakan penanda untuk membantu mengontrol jumlah pekerjaan dalam buffer perintah.
Perhatikan bahwa aplikasi harus memberikan perhatian khusus pada biaya besar yang terkait dengan pembersihan buffer perintah karena ini menyebabkan sistem operasi beralih ke mode kernel, sehingga menimbulkan penalti performa yang cukup besar. Aplikasi juga harus menyadari membuang-buang siklus CPU dengan menunggu kueri selesai.
Kueri adalah pengoptimalan yang akan digunakan selama penyajian untuk meningkatkan performa. Oleh karena itu, tidak bermanfaat untuk menghabiskan waktu menunggu kueri selesai. Jika kueri dikeluarkan dan jika hasilnya belum siap pada saat aplikasi memeriksanya, upaya pengoptimalan tidak berhasil dan penyajian harus berlanjut seperti biasa.
Contoh klasik dari ini adalah selama Oklusi Culling. Alih-alih perulangan di atas, aplikasi yang menggunakan kueri dapat menerapkan oklusi culling untuk memeriksa untuk melihat apakah kueri telah selesai pada saat dibutuhkan hasilnya. Jika kueri belum selesai, lanjutkan (sebagai skenario terburuk) seolah-olah objek yang diuji terhadap tidak terpakai (yaitu terlihat) dan merendernya. Kode akan terlihat mirip dengan yang berikut ini.
IDirect3DQuery9* pOcclusionQuery = NULL;
m_pD3DDevice->CreateQuery( D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery );
// Add a begin marker to the command buffer queue.
pOcclusionQuery->Issue( D3DISSUE_BEGIN );
... // API calls
// Add an end marker to the command buffer queue.
pOcclusionQuery->Issue( D3DISSUE_END );
// Avoid flushing and letting the CPU go idle by not using a while loop.
// Check if queries are finished:
DWORD dwOccluded = 0;
if( S_FALSE == pOcclusionQuery->GetData( &dwOccluded, sizeof(DWORD), 0 ) )
{
// Query is not done yet or object not occluded; avoid flushing/wait by continuing with worst-case scenario
pSomeComplexMesh->Render();
}
else if( dwOccluded != 0 )
{
// Query is done and object is not occluded.
pSomeComplexMesh->Render();
}