Bagikan melalui


Aplikasi Direct2D Multithreaded

Jika Anda mengembangkan aplikasi Direct2D , Anda mungkin perlu mengakses sumber daya Direct2D dari lebih dari satu utas. Dalam kasus lain, Anda mungkin ingin menggunakan multi-utas untuk mendapatkan performa yang lebih baik atau responsivitas yang lebih baik (seperti menggunakan satu utas untuk tampilan layar dan utas terpisah untuk penyajian offline).

Topik ini menjelaskan praktik terbaik untuk mengembangkan aplikasi Direct2D multithreaded dengan sedikit atau tanpa penyajian Direct3D . Cacat perangkat lunak yang disebabkan oleh masalah konkurensi dapat sulit dilacak, dan sangat membantu untuk merencanakan kebijakan multithreading Anda dan untuk mengikuti praktik terbaik yang dijelaskan di sini.

Catatan

Jika Anda mengakses dua sumber daya Direct2D yang dibuat dari dua pabrik Direct2D berulir tunggal yang berbeda, itu tidak menyebabkan konflik akses selama perangkat Direct3D dan konteks perangkat yang mendasar juga berbeda. Ketika berbicara tentang "mengakses sumber daya Direct2D" dalam artikel ini, itu benar-benar berarti "mengakses sumber daya Direct2D yang dibuat dari Perangkat Direct2D yang sama" kecuali dinyatakan sebaliknya.

Mengembangkan Aplikasi Thread-Safe yang Hanya Memanggil API Direct2D

Anda dapat membuat instans pabrik Direct2D multithreaded. Anda dapat menggunakan dan berbagi pabrik multithreaded dan semua sumber dayanya dari lebih dari satu utas, tetapi akses ke sumber daya tersebut (melalui panggilan Direct2D) diserialisasikan oleh Direct2D, sehingga tidak ada konflik akses yang terjadi. Jika aplikasi Anda hanya memanggil API Direct2D, perlindungan tersebut secara otomatis dilakukan oleh Direct2D dalam tingkat terperinci dengan overhead minimum. Kode untuk membuat pabrik multithreaded di sini.

ID2D1Factory* m_D2DFactory;

// Create a Direct2D factory.
HRESULT hr = D2D1CreateFactory(
    D2D1_FACTORY_TYPE_MULTI_THREADED,
    &m_D2DFactory
);

Gambar di sini menunjukkan bagaimana Direct2D menserialisasikan dua utas yang melakukan panggilan hanya menggunakan API Direct2D.

diagram dua utas berseri.

Mengembangkan Aplikasi Thread-Safe Direct2D dengan Panggilan Direct3D atau DXGI minimal

Lebih dari sesering mungkin bahwa aplikasi Direct2D juga melakukan beberapa panggilan Direct3D atau DXGI. Misalnya, utas tampilan akan menggambar di Direct2D lalu hadir menggunakan rantai pertukaran DXGI.

Dalam hal ini, memastikan keamanan utas lebih rumit: beberapa panggilan Direct2D secara tidak langsung mengakses sumber daya Direct3D yang mendasar, yang mungkin diakses secara bersamaan oleh utas lain yang memanggil Direct3D atau DXGI. Karena panggilan Direct3D atau DXGI tersebut berada di luar kesadaran dan kontrol Direct2D, Anda perlu membuat pabrik Direct2D multithreaded, tetapi Anda harus melakukan mor untuk menghindari konflik akses.

Diagram di sini menunjukkan konflik akses sumber daya Direct3D karena utas T0 mengakses sumber daya secara tidak langsung melalui panggilan Direct2D dan T2 mengakses sumber daya yang sama langsung melalui panggilan Direct3D atau DXGI.

Catatan

Perlindungan utas yang disediakan Direct2D (kunci biru dalam gambar ini) tidak membantu dalam hal ini.

 

diagram perlindungan utas.

Untuk menghindari konflik akses sumber daya di sini, kami sarankan Anda secara eksplisit memperoleh kunci yang digunakan Direct2D untuk sinkronisasi akses internal, dan menerapkan kunci tersebut ketika utas perlu melakukan panggilan Direct3D atau DXGI yang dapat menyebabkan konflik akses seperti yang ditunjukkan di sini. Secara khusus, Anda harus berhati-hati dengan kode yang menggunakan pengecualian atau sistem awal berdasarkan kode pengembalian HRESULT. Untuk alasan ini, kami sarankan Anda menggunakan pola RAII (Resource Acquisition Is Initialization) untuk memanggil metode Enter and Leave .

Catatan

Penting agar Anda memasangkan panggilan ke metode Enter and Leave , jika tidak, aplikasi Anda dapat melakukan kebuntuan.

 

Kode di sini menunjukkan contoh kapan harus mengunci dan kemudian membuka kunci di sekitar panggilan Direct3D atau DXGI.

void MyApp::DrawFromThread2()
{
    // We are accessing Direct3D resources directly without Direct2D's knowledge, so we
    // must manually acquire and apply the Direct2D factory lock.
    ID2D1Multithread* m_D2DMultithread;
    m_D2DFactory->QueryInterface(IID_PPV_ARGS(&m_D2DMultithread));
    m_D2DMultithread->Enter();
    
    // Now it is safe to make Direct3D/DXGI calls, such as IDXGISwapChain::Present
    MakeDirect3DCalls();

    // It is absolutely critical that the factory lock be released upon
    // exiting this function, or else any consequent Direct2D calls will be blocked.
    m_D2DMultithread->Leave();
}

Catatan

Beberapa panggilan Direct3D atau DXGI (terutama IDXGISwapChain::P resent) dapat memperoleh kunci dan/atau memicu panggilan balik ke dalam kode fungsi atau metode panggilan. Anda harus menyadari hal ini dan memastikan bahwa perilaku tersebut tidak menyebabkan kebuntuan. Untuk informasi selengkapnya, lihat topik Gambaran Umum DXGI .

 

diagram penguncian utas direct2d dan direct3d.

Saat Anda menggunakan metode Enter and Leave , panggilan dilindungi oleh Direct2D otomatis dan kunci eksplisit, sehingga aplikasi tidak mengalami konflik akses.

Ada pendekatan lain untuk mengatasi masalah ini. Namun, kami sarankan Anda secara eksplisit menjaga panggilan Direct3D atau DXGI dengan kunci Direct2D karena biasanya memberikan performa yang lebih baik karena melindungi konkurensi pada tingkat yang jauh lebih halus dan dengan overhead yang lebih rendah di bawah penutup Direct2D.

Memastikan Atomitas Operasi Stateful

Meskipun fitur keamanan utas DirectX dapat membantu memastikan bahwa tidak ada dua panggilan API individual yang dilakukan secara bersamaan, Anda juga harus memastikan bahwa utas yang membuat panggilan API stateful tidak mengganggu satu sama lain. Berikut adalah contohnya.

  1. Ada dua baris teks yang ingin Anda render ke layar (berdasarkan Utas 0) dan di luar layar (oleh Utas 1): Baris #1 adalah "A lebih besar" dan Baris #2 adalah "daripada B", yang keduanya akan digambar menggunakan kuas hitam padat.
  2. Utas 1 menggambar baris pertama teks.
  3. Utas 0 bereaksi terhadap input pengguna, memperbarui kedua baris teks menjadi "B lebih kecil" dan "dari A" masing-masing, dan mengubah warna kuas menjadi merah pekat untuk gambarnya sendiri;
  4. Utas 1 terus menggambar baris kedua teks, yang sekarang "daripada A", dengan kuas warna merah;
  5. Akhirnya, kita mendapatkan dua baris teks pada target gambar di luar layar: "A lebih besar" hitam dan "daripada A" berwarna merah.

diagram utas layar aktif dan nonaktif.

Di baris atas, Utas 0 digambar dengan string teks saat ini dan kuas hitam saat ini. Utas 1 hanya menyelesaikan gambar di luar layar di bagian atas.

Di baris tengah, Utas 0 merespons interaksi pengguna, memperbarui string teks dan kuas, lalu me-refresh layar. Pada titik ini, Thread 1 diblokir. Di baris bawah, penyajian akhir di luar layar setelah Thread 1 melanjutkan menggambar bagian bawah dengan kuas yang diubah dan string teks yang diubah.

Untuk mengatasi masalah ini, kami sarankan Anda memiliki konteks terpisah untuk setiap utas, sehingga:

  • Anda harus membuat salinan konteks perangkat sehingga sumber daya yang dapat diubah (yaitu sumber daya yang dapat bervariasi selama tampilan atau pencetakan, seperti konten teks atau kuas warna solid dalam contoh) tidak berubah saat Anda merender. Dalam sampel ini, Anda harus mempertahankan salinan dua baris teks dan kuas warna sebelum Anda menggambar. Dengan demikian, Anda menjamin setiap utas memiliki konten yang lengkap dan konsisten untuk digambar dan disajikan.
  • Anda harus berbagi sumber daya berat (seperti bitmap dan grafik efek kompleks) yang diinisialisasi sekali dan kemudian tidak pernah dimodifikasi di seluruh utas untuk meningkatkan performa.
  • Anda dapat berbagi sumber daya ringan (seperti kuas warna solid dan format teks) yang diinisialisasi sekali dan kemudian tidak pernah dimodifikasi di seluruh utas atau tidak

Ringkasan

Saat Anda mengembangkan aplikasi Direct2D multithreaded, Anda harus membuat pabrik Direct2D multithreaded lalu memperoleh semua sumber daya Direct2D dari pabrik tersebut. Jika utas akan melakukan panggilan Direct3D atau DXGI, Anda juga harus secara eksplisit memperoleh maka terapkan kunci Direct2D untuk menjaga panggilan Direct3D atau DXGI tersebut. Selain itu, Anda harus memastikan integritas konteks dengan memiliki salinan sumber daya yang dapat diubah untuk setiap utas.