Cara tercepat untuk membuat garis dengan AA, ketebalan bervariasi dalam DirectX

14

Jadi saya melakukan beberapa pengembangan DirectX, menggunakan SharpDX di bawah. NET tepatnya (tetapi solusi DirectX / C ++ API berlaku). Saya mencari cara tercepat untuk membuat baris dalam proyeksi ortogonal (misalnya mensimulasikan gambar garis 2D untuk aplikasi ilmiah) menggunakan DirectX.

Tangkapan layar jenis plot yang saya coba buat berikut: masukkan deskripsi gambar di sini

Tidak jarang plot semacam ini memiliki garis dengan jutaan segmen, dengan ketebalan bervariasi, dengan atau tanpa antialiasing per-garis (atau layar penuh AA hidup / mati). Saya perlu memperbarui simpul untuk garis sangat sering (mis. 20 kali / detik) dan membongkar GPU sebanyak mungkin.

Sejauh ini saya sudah mencoba:

  1. Render perangkat lunak, mis. GDI + sebenarnya bukan kinerja yang buruk tetapi jelas berat pada CPU
  2. Direct2D API - lebih lambat dari GDI, terutama dengan Antialiasing aktif
  3. Direct3D10 menggunakan metode ini untuk meniru AA menggunakan warna titik dan tessellation di sisi CPU. Juga lambat (saya memprofilkannya dan 80% waktu dihabiskan untuk menghitung posisi titik)

Untuk metode ke-3 saya menggunakan Vertex Buffer untuk mengirim strip segitiga ke GPU dan memperbarui setiap 200ms dengan simpul baru. Saya mendapatkan kecepatan refresh sekitar 5FPS untuk 100.000 segmen garis. Saya butuh jutaan idealnya!

Sekarang saya berpikir bahwa cara tercepat adalah melakukan penghentian pada GPU, misalnya dalam Geometry Shader. Saya bisa mengirim simpul sebagai daftar garis atau paket dalam tekstur dan membongkar dalam Geometry Shader untuk membuat paha depan. Atau, cukup kirim poin mentah ke pixel shader dan terapkan menggambar Garis Bresenham sepenuhnya dalam pixel shader. HLSL saya berkarat, model shader 2 dari 2006 jadi saya tidak tahu tentang hal-hal gila yang bisa dilakukan GPU modern.

Jadi pertanyaannya adalah: - Adakah yang pernah melakukan ini sebelumnya, dan apakah Anda punya saran untuk dicoba? - Apakah Anda memiliki saran untuk meningkatkan kinerja dengan memperbarui geometri dengan cepat (mis. Daftar simpul baru setiap 20 ms)?

PEMBARUAN 21 Januari

Saya telah menerapkan metode (3) di atas menggunakan Geometri shader menggunakan LineStrip dan Dynamic Vertex Buffer. Sekarang saya mendapatkan 100FPS pada 100k poin dan 10FPS pada 1.000.000 poin. Ini adalah peningkatan besar tapi sekarang saya mengisi dan menghitung terbatas, jadi saya memikirkan teknik / ide lain.

  • Bagaimana dengan Pemunculan Perangkat Keras geometri Segmen Garis?
  • Bagaimana dengan Sprite Batch?
  • Bagaimana dengan metode berorientasi (Pixel shader) lainnya?
  • Bisakah saya memusnahkan GPU atau CPU secara efisien?

Komentar & saran Anda sangat kami hargai!

ABT
sumber
1
Bagaimana dengan AA asli di Direct3D?
API-Beast
5
Bisakah Anda menunjukkan beberapa contoh kurva yang ingin Anda plot? Anda menyebutkan satu juta simpul tetapi layar Anda mungkin tidak memiliki lebih dari satu juta piksel , apakah kuantitas itu benar-benar dibutuhkan? Menurut saya, Anda tidak membutuhkan kepadatan data lengkap di mana pun. Sudahkah Anda mempertimbangkan LOD?
sam hocevar
1
Pendekatan kedua yang Anda tautkan tampak bagus, apakah Anda menggunakan vertex buffer dinamis yang Anda perbarui, atau apakah Anda membuat yang baru setiap kali?
1
Anda mungkin harus menggunakan buffer vertex dinamis (tidak banyak pekerjaan, katakan saja buffer vertex Anda menjadi dinamis, petakan buffer, salin data Anda, hapus peta) tetapi jika bottleneck Anda adalah generasi vertex di tempat pertama, yang mungkin menang dapat banyak membantu sekarang. Jangan berpikir ada yang gila menggunakan GS untuk meringankan beban pada CPU
1
Jika jumlah simpul pada akhirnya terlalu besar untuk GPU, Anda masih bisa mencoba downsampling input Anda - Anda tidak benar-benar membutuhkan ratusan juta segmen garis untuk satu kurva ketika layar Anda akan menjadi beberapa ribu piksel lebarnya. paling banyak.

Jawaban:

16

Jika Anda akan membuat Y = f(X)grafik saja, maka saya sarankan mencoba metode berikut.

Data kurva dilewatkan sebagai data tekstur , menjadikannya persisten, dan memungkinkan pembaruan parsial glTexSubImage2Dmisalnya. Jika Anda perlu menggulir, Anda bahkan dapat menerapkan buffer melingkar dan hanya memperbarui beberapa nilai per frame. Setiap kurva ditampilkan sebagai quad layar penuh dan semua pekerjaan dilakukan oleh pixel shader.

Konten tekstur satu komponen dapat terlihat seperti ini:

+----+----+----+----+
| 12 | 10 |  5 | .. | values for curve #1
+----+----+----+----+
| 55 | 83 | 87 | .. | values for curve #2
+----+----+----+----+

Pekerjaan pixel shader adalah sebagai berikut:

  • menemukan koordinat X dari fragmen saat ini di ruang dataset
  • ambil mis. 4 titik data terdekat yang memiliki data; misalnya jika nilai X adalah 41.3itu akan memilih 40, 41, 42dan 43.
  • kueri tekstur untuk nilai 4 Y (pastikan sampler tidak melakukan interpolasi dalam bentuk apa pun)
  • ubah X,Ypasangan menjadi ruang layar
  • hitung jarak dari fragmen saat ini ke masing-masing dari tiga segmen dan empat titik
  • gunakan jarak sebagai nilai alpha untuk fragmen saat ini

Anda mungkin ingin mengganti 4 dengan nilai yang lebih besar tergantung pada level zoom potensial.

Saya telah menulis shader GLSL yang sangat cepat dan kotor yang mengimplementasikan fitur ini . Saya dapat menambahkan versi HLSL nanti, tetapi Anda harus dapat mengonversinya tanpa terlalu banyak usaha. Hasilnya dapat dilihat di bawah, dengan ukuran garis dan kepadatan data yang berbeda:

kurva

Satu keuntungan yang jelas adalah bahwa jumlah data yang ditransfer sangat rendah, dan jumlah drawcall hanya satu.

sam hocevar
sumber
Itu adalah teknik yang sangat keren - saya telah melakukan GPGPU sebelumnya sehingga mengepak tekstur dengan data adalah sesuatu yang saya ketahui, tetapi bukan sesuatu yang saya pertimbangkan untuk ini. Whats ukuran terbesar (mis. Dataset terbesar yang dapat Anda unggah?). Saya akan mengunduh kode Anda jika Anda tidak keberatan dan mempermainkannya - Saya ingin sekali melihat kinerjanya.
Dr. ABT
@ Dr.ABT Ukuran dataset hanya dibatasi oleh ukuran tekstur maksimum. Saya tidak melihat mengapa jutaan titik tidak akan berfungsi mengingat beberapa tata letak data yang tepat. Perhatikan bahwa kode saya tentu saja bukan kualitas produksi tetapi jangan ragu untuk menghubungi saya secara pribadi jika ada masalah.
sam hocevar
Cheers @SamHocevar - Saya akan mencobanya. Sudah lama sejak saya mengkompilasi contoh pikiran OpenGL! :-)
Dr. ABT
Menandai sebagai jawaban, seolah-olah saya tidak menggunakan beban tekstur, Anda menyajikan contoh OpenGL nyata yang bisa menggambar garis pada pixel shader dan menempatkan kami ke arah yang benar !! :)
Dr. ABT
4

Ada bab Permata GPU tentang rendering baris antialiased: Fast Prefiltered Lines . Ide dasarnya adalah membuat setiap segmen garis sebagai kuad dan menghitung, pada setiap piksel, fungsi Gaussian dari jarak pusat piksel dari segmen garis.

Ini berarti menggambar setiap segmen garis dalam grafik sebagai quad yang terpisah, tetapi dalam D3D11 Anda tentu bisa menggunakan shader geometri dan / atau membuat instans untuk menghasilkan paha depan, mengurangi jumlah data yang akan ditransfer ke GPU hanya pada titik data diri. Saya mungkin akan mengatur titik data sebagai StructuredBuffer untuk dibaca oleh vertex / geometri shader, kemudian lakukan draw call yang menentukan jumlah segmen yang akan diambil. Tidak akan ada buffer vertex yang sebenarnya; vertex shader hanya akan menggunakan SV_VertexID atau SV_InstanceID untuk menentukan titik data yang akan dilihat.

Nathan Reed
sumber
Hai terima kasih - ya saya tahu artikel itu, sayangnya tidak ada kode sumbernya :-( Premis GeoShader + FragShader tampaknya menjadi pemenang untuk jenis pekerjaan ini. Mendapatkan data ke GPU secara efisien akan menjadi bagian yang sulit!
Dr. ABT
Hei @NathanReed kembali ke ini - jika saya menggunakan Geometry Shader atau membuat instans untuk menggambar quads, maka Point1 / Point2 adalah simpul yang berlawanan dari quad, bagaimana dalam Pixel Shader saya dapat menghitung jarak ke garis antara Pt1 & Pt2? Apakah pixel shader memiliki pengetahuan tentang input geometri?
Dr. ABT
@ Dr.ABT Parameter dari garis matematika harus dikirim ke pixel shader melalui interpolator. Pixel shader tidak memiliki akses langsung ke geometri.
Nathan Reed
Saya mengerti, dapatkah ini dilakukan dengan mengirimkan output dari GeometryShader atau instancing? Untuk kredit tambahan (jika Anda tertarik), saya menambahkan Q di sini: gamedev.stackexchange.com/questions/47831/…
Dr. ABT
@ Dr.ABT Sama persis dengan tekstur koordinat, vektor normal, dan semua hal semacam itu yang biasanya dikirim ke pixel shader - dengan menulis output dari vertex / geometri shader yang diinterpolasi dan input ke pixel shader.
Nathan Reed
0

@ Dr.ABT - Permintaan maaf untuk ini adalah pertanyaan, bukan jawaban. Saya tidak menemukan cara untuk menanyakannya dalam jawaban oleh Sam Hocevar di atas.

Bisakah Anda berbagi detail lebih lanjut tentang bagaimana Anda akhirnya menerapkan garis untuk bagan Anda? Saya memiliki kebutuhan yang sama untuk aplikasi WPF. Terima kasih sebelumnya untuk setiap detail atau kode yang dapat Anda bagikan tentang ini.

ErcGeek
sumber
seperti yang Anda tahu itu bukan jawaban, harap edit dan taruh sebagai komentar
MephistonX
Saya mencoba melakukan itu tetapi tidak ada tombol "tambahkan komentar" pada posting asli.
ErcGeek
Hai ErcGeek, Maaf saya tidak dapat membagikan solusi terakhir karena informasi ini adalah hak milik perusahaan saya. Meskipun contoh dan saran di atas menempatkan kami di jalur yang benar. Semua yang terbaik!
Dr. ABT
Anda tidak dapat memposting komentar sebelum Anda mencapai reputasi tertentu. Dan semua downvotes ini pasti tidak akan memberi Anda kesempatan itu.
Mathias Lykkegaard Lorenzen
Lelaki itu tidak boleh dihukum karena ingin mengetahui hasil akhirnya dan memintanya dengan satu-satunya cara yang tersedia baginya. Terpilih jawabannya.
David Ching