Di sini saya akan memposting terjemahan manual Vulkan API. Tautan sumber - vulkan-tutorial.com . Karena pengguna Habr lainnya, kiwhy (https://habr.com/ru/users/kiwhy/), terlibat dalam penerjemahan manual yang sama, kami setuju untuk
berbagi pelajaran di antara kami sendiri. Dalam publikasi saya, saya akan memberikan tautan ke bab-bab yang diterjemahkan oleh kiwhy.
Kandungan
1. Perkenalan
Lihat artikel oleh penulis kiwhy - habr.com/ru/post/462137
2. Ikhtisar
Latar belakang Vulkan
Bagaimana cara menggambar segitiga?
- Langkah 1 - Instance dan Perangkat Fisik
- Langkah 2 - Unit Logika dan Keluarga Antrian
- Langkah 3 - Permukaan jendela dan tukar rantai
- Langkah 4 - Tampilan gambar dan framebuffers
- Langkah 5 - Render Pass
- Langkah 6 - Pipa Grafik
- Langkah 7 - Command Pool dan Command Buffer
- Langkah 8 - loop utama
- kesimpulan
Konsep API
Dalam bab ini, kita akan mulai menggunakan Vulkan dan melihat masalah apa yang dapat diselesaikannya. Kami akan menjelaskan langkah-langkah yang diperlukan untuk membuat segitiga pertama Anda. Ini akan memberi Anda gambaran umum tentang standar dan memungkinkan Anda untuk memahami logika di balik tata letak bab-bab berikutnya. Kami menyimpulkan dengan melihat struktur Vulkan API dan kasus penggunaan umum.
Prasyarat untuk Vulkan
Seperti API grafis sebelumnya, Vulkan dipahami sebagai abstraksi lintas platform melalui GPU . Masalah utama dengan sebagian besar API ini adalah bahwa selama pengembangannya, mereka menggunakan perangkat keras grafis yang terbatas pada fungsionalitas tetap. Pengembang harus menyediakan data vertex dalam format standar dan sepenuhnya bergantung pada produsen GPU untuk penerangan dan bayangan.
Seiring dengan berkembangnya arsitektur kartu video, semakin banyak fungsi yang dapat diprogram mulai muncul di dalamnya. Semua fitur baru perlu digabungkan dengan API yang ada dalam beberapa cara. Hal ini menyebabkan abstraksi yang tidak sempurna dan banyak hipotesis di pihak driver grafis tentang bagaimana menerjemahkan maksud programmer ke dalam arsitektur grafis modern. Oleh karena itu, sejumlah besar pembaruan driver dirilis untuk meningkatkan kinerja dalam game. Karena kerumitan driver tersebut, seringkali terdapat perbedaan di antara vendor, misalnya, dalam sintaksis yang diadopsi untuk shader... Selain itu, dekade terakhir ini juga terlihat masuknya perangkat seluler dengan perangkat keras grafis yang kuat. Arsitektur dari GPU seluler ini dapat sangat bervariasi tergantung pada ukuran dan kebutuhan daya. Salah satu contohnya adalah rendering ubin , yang dapat memberikan kinerja yang lebih baik melalui kontrol yang lebih baik atas fungsionalitas. Batasan lain karena usia API adalah dukungan terbatas untuk multithreading, yang dapat menyebabkan kemacetan di sisi CPU.
Vulkan membantu menyelesaikan masalah ini karena dibuat dari awal untuk arsitektur grafis modern. Hal ini mengurangi overhead sisi pengemudi dengan memungkinkan developer mendeskripsikan tujuan mereka dengan jelas menggunakan API verbose. Vulkan memungkinkan Anda membuat dan mengirim perintah secara paralel di beberapa utas. Ini juga mengurangi perbedaan kompilasi antara shader dengan berpindah ke format bytecode standar dan menggunakan kompiler tunggal. Terakhir, Vulkan menyatukan kapabilitas inti kartu grafis masa kini dengan mengintegrasikan grafis dan kapabilitas komputasi ke dalam satu API.
Bagaimana cara menggambar segitiga?
Kami akan melihat sekilas langkah-langkah yang diperlukan untuk menggambar segitiga. Ini akan memberi Anda gambaran umum tentang prosesnya. Penjelasan rinci dari setiap konsep akan diberikan pada bab-bab berikut.
Langkah 1 - Instance dan Perangkat Fisik yang
Bekerja dengan Vulkan dimulai dengan mengonfigurasi Vulkan API melalui VkInstance (instance). Sebuah instance dibuat menggunakan deskripsi program Anda dan ekstensi apa pun yang ingin Anda gunakan. Setelah pembuatan instance, Anda dapat menanyakan hardware yang didukung Vulkan dan memilih satu atau beberapa VkPhysicalDevices untuk menjalankan operasi. Anda dapat menanyakan tentang parameter seperti ukuran VRAM dan kemampuan perangkat untuk memilih perangkat yang Anda inginkan jika Anda lebih suka menggunakan kartu grafis khusus.
Langkah 2 - Perangkat Logika dan Keluarga Antrian
Setelah Anda memilih perangkat keras yang sesuai untuk digunakan, Anda perlu membuat VkDevice (perangkat logis), di mana Anda akan menjelaskan secara lebih rinci fitur apa ( VkPhysicalDeviceFeatures ) yang akan Anda gunakan, misalnya, rendering ke beberapa area pandang. s (rendering multi viewport) dan float 64-bit. Anda juga perlu menetapkan keluarga antrian mana yang ingin Anda gunakan. Banyak operasi yang dilakukan dengan Vulkan, seperti perintah menggambar dan operasi dalam memori, dilakukan secara asinkron setelah dikirim ke VkQueue... Antrian dialokasikan dari keluarga antrian, di mana setiap keluarga mendukung serangkaian operasi tertentu. Misalnya, keluarga antrian yang terpisah mungkin ada untuk operasi grafik, operasi komputasi, dan transfer data memori. Selain itu, ketersediaannya dapat digunakan sebagai parameter kunci saat memilih perangkat fisik. Beberapa perangkat berkemampuan Vulkan tidak menawarkan kemampuan grafis apa pun, namun, semua kartu grafis berkemampuan Vulkan modern umumnya mendukung semua operasi antrian yang kami butuhkan.
Langkah 3 - Permukaan jendela dan rantai pertukaran
Jika Anda tertarik pada lebih dari sekadar rendering di luar layar, Anda perlu membuat jendela untuk menampilkan gambar yang diberikan. Windows dapat dibuat menggunakan API atau pustaka platform asli seperti GLFW dan SDL . Kami akan menggunakan GLFW untuk tutorial ini, yang akan kami bahas lebih detail di bab berikutnya.
Kita membutuhkan dua komponen lagi untuk dirender ke jendela aplikasi: permukaan jendela (
VkSurfaceKHR) dan rantai pertunjukan ( VkSwapchainKHR). Perhatikan postfixKHRyang menunjukkan bahwa objek ini adalah bagian dari ekstensi Vulkan. Vulkan API sepenuhnya tidak bergantung pada platform, jadi kita perlu menggunakan ekstensi standar WSI (Window System Interface) untuk berinteraksi dengan window manager. Surface adalah abstraksi jendela lintas platform untuk rendering, yang biasanya dibuat dengan mereferensikan pegangan jendela asli, misalnya HWNDdi Windows. Untungnya, pustaka GLFW memiliki fungsi bawaan untuk bekerja dengan detail spesifik platform.
Rantai pertunjukan adalah sekumpulan target render. Tugasnya adalah memastikan bahwa gambar yang sedang ditampilkan berbeda dari yang ditampilkan di layar. Ini memungkinkan Anda untuk melacak bahwa hanya gambar yang ditampilkan yang ditampilkan. Setiap kali kita perlu membuat bingkai, kita harus membuat permintaan rantai pertunjukan untuk memberi kita gambar untuk dirender. Setelah bingkai dibuat, gambar dikembalikan ke rantai tampilan untuk ditampilkan di layar di beberapa titik. Jumlah target rendering dan kondisi untuk menampilkan gambar jadi di layar bergantung pada mode saat ini. Mode ini mencakup buffering ganda (vsync) dan buffering tiga kali lipat. Kami akan membahasnya di bab tentang membuat rantai pertunjukan.
Beberapa platform memungkinkan rendering langsung ke layar melalui ekstensi
VK_KHR_displaydan VK_KHR_display_swapchaintanpa berinteraksi dengan pengelola jendela mana pun. Ini memungkinkan Anda membuat permukaan yang mewakili keseluruhan layar dan dapat digunakan, misalnya, untuk menerapkan pengelola jendela Anda sendiri.
Langkah 4 - Tampilan gambar dan bingkai bingkai
Untuk menggambar ke dalam gambar yang diperoleh dari rantai tampilan, kita harus membungkusnya dalam VkImageView dan VkFramebuffer . Tampilan gambar mengacu pada bagian tertentu dari gambar yang digunakan, dan framebuffer mengacu pada tampilan gambar, yang digunakan sebagai penyangga warna, kedalaman, dan stensil. Karena bisa ada banyak gambar berbeda dalam rantai tampilan, kita akan membuat tampilan gambar dan framebuffer untuk masing-masing terlebih dahulu dan memilih gambar yang diperlukan selama menggambar.
Langkah 5 - Render
Passes render pass Vulkan mendeskripsikan jenis gambar yang digunakan selama operasi render, cara penggunaannya, dan cara penanganan kontennya. Sebelum menggambar segitiga, kami memberi tahu Vulkan bahwa kami ingin menggunakan satu gambar sebagai penyangga warna dan kami perlu menghapusnya sebelum menggambar. Jika render pass hanya mendeskripsikan jenis gambar yang digunakan sebagai buffer, maka VkFramebuffer sebenarnya mengaitkan gambar tertentu dengan slot tersebut.
Langkah 6 - The Graphics Pipeline Pipeline
grafik di Vulkan dikonfigurasi dengan membuat objek VkPipeline . Ini menjelaskan status yang dapat dikonfigurasi dari kartu video, seperti ukuran viewport atau operasi buffer kedalaman, serta status yang dapat diprogram menggunakan objek VkShaderModule . Objek VkShaderModule dibuat dari bytecode shader. Pengemudi juga perlu menentukan target render yang akan digunakan dalam pipeline. Kami mengaturnya dengan mereferensikan render pass.
Salah satu fitur Vulkan yang paling membedakan dibandingkan API yang ada adalah bahwa hampir semua setelan pipeline grafik sistem harus dikonfigurasi sebelumnya. Ini berarti bahwa jika Anda ingin beralih ke shader yang berbeda atau sedikit mengubah tata letak vertex, Anda perlu membuat ulang pipeline grafis sepenuhnya. Oleh karena itu, Anda harus membuat banyak objek VkPipeline terlebih dahulu untuk semua kombinasi yang diperlukan untuk operasi rendering. Hanya beberapa pengaturan dasar, seperti ukuran area pandang dan warna bening, yang dapat diubah secara dinamis. Semua negara bagian harus dijelaskan secara eksplisit. Jadi, misalnya, tidak ada status campuran warna default.
Untungnya, karena prosesnya lebih seperti kompilasi yang akan datang, alih-alih mengompilasi dengan cepat, driver memiliki lebih banyak peluang pengoptimalan dan performa lebih dapat diprediksi karena perubahan status yang signifikan, seperti beralih ke pipeline grafis yang berbeda, ditentukan secara eksplisit.
Langkah 7 - Command Pool dan Command Buffer
Seperti yang disebutkan, banyak operasi di Vulkan, seperti operasi menggambar, harus diantrekan. Sebelum mengirim operasi, operasi tersebut harus ditulis ke VkCommandBuffer . Buffer perintah berasal dari VkCommandPool , yang dikaitkan dengan keluarga antrian tertentu. Untuk menggambar segitiga sederhana, kita perlu menulis buffer perintah dengan operasi berikut:
- Mulai render pass
- Bind grafik pipa
- Gambar 3 simpul
- Akhiri render pass
Karena instance gambar di framebuffer bergantung pada gambar mana yang akan diberikan oleh rantai tampilan kepada kita, kita perlu menulis buffer perintah untuk setiap gambar yang memungkinkan dan memilih gambar yang kita perlukan selama menggambar. Kita dapat menulis buffer perintah setiap saat untuk setiap frame, tetapi ini kurang efisien.
Langkah 8 - Loop Utama
Setelah kita mengirimkan perintah gambar ke buffer perintah, loop utama tampaknya cukup sederhana. Pertama, kita mendapatkan gambar dari rantai pertunjukan dengan
vkAcquireNextImageKHR. Kami kemudian dapat memilih buffer perintah yang sesuai untuk gambar ini dan menjalankannya dengan vkQueueSubmit . Terakhir, kami mengembalikan gambar ke rantai tampilan untuk ditampilkan menggunakan vkQueuePresentKHR.
Operasi yang dikirim ke antrian dilakukan secara asinkron. Oleh karena itu, kita harus menggunakan objek sinkronisasi - semaphore - untuk memastikan urutan startup yang benar. Perlu untuk mengonfigurasi eksekusi buffer perintah gambar sedemikian rupa sehingga hanya dilakukan setelah gambar diambil dari rantai tampilan, jika tidak, situasi dapat muncul saat kita mulai membuat gambar yang masih dibaca untuk ditampilkan di layar. Panggilan
vkQueuePresentKHR, pada gilirannya, harus menunggu rendering selesai, yang akan kita gunakan semafor kedua. Ini akan memberi tahu tentang akhir rendering.
Kesimpulan
Gambaran singkat ini memberi Anda gambaran umum tentang pekerjaan sebelum menggambar segitiga pertama Anda. Kenyataannya, masih banyak langkah lagi. Ini termasuk mengalokasikan buffer vertex, membuat buffer seragam, dan memuat gambar tekstur - yang semuanya akan kita bahas di bab berikutnya, tetapi untuk sekarang, mari kita mulai dengan sederhana. Semakin jauh kita bergerak, semakin sulit materialnya. Perhatikan bahwa kami memutuskan untuk melakukan cara yang rumit dengan awalnya menyematkan koordinat vertex di vertex shader alih-alih menggunakan buffer vertex. Keputusan ini disebabkan oleh fakta bahwa untuk mengelola buffer simpul, Anda harus terlebih dahulu terbiasa dengan buffer perintah.
Mari kita rangkum secara singkat. Untuk menggambar segitiga pertama, kita membutuhkan:
- Buat VkInstance
- Pilih kartu video yang didukung ( VkPhysicalDevice )
- Buat VkDevice dan VkQueue untuk menggambar dan menampilkan
- Buat jendela, permukaan jendela dan rantai pertunjukan
- Bungkus gambar rantai tampilan di VkImageView
- Buat render pass yang menentukan target render dan penggunaannya
- Buat framebuffer untuk render pass
- Konfigurasi pipeline grafis
- Distribusikan dan tulis perintah gambar ke buffer untuk setiap gambar di rantai tampilan
- Render frame ke gambar yang diterima dengan mengirimkan buffer perintah yang benar dan mengembalikan gambar ke rantai tampilan
Terlepas dari kenyataan bahwa ada banyak langkah, artinya masing-masing akan jelas pada bab-bab selanjutnya. Jika Anda tidak dapat menemukan langkah, kembali ke bab ini.
Konsep API
Bab ini akan diakhiri dengan ikhtisar singkat tentang bagaimana Vulkan API disusun di tingkat yang lebih rendah.
Standar Pengkodean
Semua fungsi, enumerasi, dan struktur Vulkan diberi label di bawah judul
vulkan.hyang disertakan dalam Vulkan SDK yang dikembangkan oleh LunarG. Menginstal SDK akan dibahas di bab berikutnya.
Fungsi diawali
vkdengan huruf kecil, jenis enumerasi (enumerasi) dan struktur diawali Vk, dan nilai enumerasi diawali VK_. API memanfaatkan struktur secara ekstensif untuk memberikan parameter ke fungsi. Misalnya, objek biasanya dibuat sesuai dengan pola berikut:
Banyak struktur di Vulkan mengharuskan Anda menentukan secara eksplisit jenis struktur di anggota
sType. Seorang anggota pNextdapat menunjuk ke struktur ekstensi dan akan selalu menjadi tipenullptr... Fungsi yang membuat atau menghancurkan sebuah objek akan memiliki parameter VkAllocationCallbacks , yang memungkinkan Anda untuk menggunakan pengalokasi memori Anda sendiri dan yang di manual juga akan memiliki tipe nullptr.
Hampir semua fungsi mengembalikan VkResult , yang merupakan
VK_SUCCESSkode kesalahan. Spesifikasi menyatakan kode kesalahan mana yang dapat dikembalikan setiap fungsi dan apa artinya.
Lapisan Validasi
Seperti yang disebutkan, Vulkan dirancang untuk memberikan kinerja tinggi dengan beban pengemudi yang rendah. Oleh karena itu, ini mencakup kemampuan deteksi dan koreksi kesalahan otomatis yang sangat terbatas. Jika Anda membuat kesalahan, driver akan crash atau lebih buruk lagi, terus bekerja pada kartu grafis Anda, tetapi gagal pada kartu grafis lain.
Oleh karena itu, Vulkan memungkinkan Anda menjalankan validasi lanjutan menggunakan fitur yang disebut lapisan validasi... Lapisan validasi adalah potongan kode yang dapat disisipkan di antara API dan driver grafis untuk melakukan validasi tambahan pada parameter fungsi dan melacak masalah manajemen memori. Ini nyaman karena Anda dapat memulainya selama pengembangan dan kemudian menonaktifkannya sepenuhnya saat memulai program tanpa biaya tambahan. Siapa pun dapat menulis lapisan validasinya sendiri, tetapi Vulkan SDK dari LunarG menyediakan rangkaian standar yang akan kita gunakan di sepanjang tutorial. Anda juga perlu mendaftarkan fungsi panggilan balik untuk menerima pesan debug dari lapisan.
Karena operasi di Vulkan sangat detail, dan lapisan validasinya cukup luas, akan lebih mudah bagi Anda untuk menentukan penyebab layar hitam dibandingkan dengan OpenGL dan Direct3D.
Hanya ada satu langkah tersisa sebelum kita mulai membuat kode, dan itu adalah menyiapkan lingkungan pengembangan.