Bagaimana cara memuat sumber daya grafis secara asinkron?

9

Mari kita pikirkan platform-agnostik: Saya ingin memuat beberapa sumber daya grafis sementara sisa permainan sedang berjalan.

Pada prinsipnya, saya dapat memuat file aktual pada utas terpisah, atau menggunakan async I / O. Tetapi dengan objek grafis, saya harus mengunggahnya ke GPU, dan itu (biasanya) hanya dapat dilakukan pada utas utama.

Saya dapat mengubah lingkaran permainan saya agar terlihat seperti ini:

while true do
    update()
    for each pending resource do
        load resource to gpu
    end
    draw()
end

sementara memiliki sumber daya thread yang terpisah dari disk ke RAM.

Namun, jika ada banyak sumber daya yang besar untuk dimuat, ini dapat menyebabkan saya melewatkan tenggat waktu bingkai dan akhirnya mendapatkan bingkai yang dijatuhkan. Jadi saya bisa mengubah loop ke ini:

while true do
    update()
    if there are pending resources then
        load one resource to gpu
        remove that resource from the pending list
    end
    draw()
end

Secara efektif memuat hanya satu sumber daya per frame. Namun, jika ada banyak sumber daya kecil untuk dimuat, memuat semuanya akan membutuhkan banyak bingkai, dan akan ada banyak waktu terbuang.

Secara optimal, saya ingin menghitung waktu pemuatan saya dengan cara berikut:

while true do
    time_start = get_time()
    update()
    while there are pending resources then
        current_time = get_time()
        if (current_time - time_start) + time_to_load(resource) >= 1/60 then
            break
        load one resource to gpu
        remove that resource from the pending list
    end
    draw()
end

Dengan cara ini, saya hanya akan memuat sumber daya jika saya bisa melakukannya dalam waktu yang saya miliki untuk kerangka itu. Sayangnya, ini membutuhkan cara untuk memperkirakan jumlah waktu yang diperlukan untuk memuat sumber daya yang diberikan, dan sejauh yang saya tahu, biasanya tidak ada cara untuk melakukan ini.

Apa yang kulewatkan di sini? Berapa banyak game yang dapat memuat semua barang mereka sepenuhnya asinkron dan tanpa bingkai yang terjatuh atau waktu pemuatan yang sangat lama?

Piyama Panda
sumber

Jawaban:

7

Mari kita mulai dengan mengasumsikan dunia yang sempurna. Ada dua langkah untuk memuat sumber daya: pertama Anda mendapatkannya dari media penyimpanan Anda dan ke dalam memori dalam format yang benar, dan kedua Anda mentransfernya melintasi bus memori ke memori video. Tak satu pun dari kedua langkah itu benar-benar perlu menggunakan waktu di utas utama Anda — hanya perlu terlibat untuk mengeluarkan perintah I / O. CPU dan GPU Anda dapat terus melakukan hal-hal lain saat sumber daya disalin. Satu-satunya sumber daya nyata yang dikonsumsi adalah bandwidth memori.

Jika Anda menggunakan platform tanpa banyak lapisan abstraksi antara Anda dan perangkat keras, API mungkin mengekspos konsep-konsep ini secara langsung. Tetapi jika Anda menggunakan PC, mungkin ada driver yang berada di antara Anda dan GPU, dan ia ingin melakukan sesuatu dengan caranya. Bergantung pada API Anda mungkin dapat membuat tekstur yang didukung oleh memori yang Anda miliki, tetapi lebih mungkin memanggil "buat tekstur" API akan menyalin tekstur ke dalam beberapa memori yang dimiliki pengemudi. Dalam hal ini menciptakan tekstur akan memiliki beberapa overhead tetap dan beberapa waktu sebanding dengan ukuran tekstur. Setelah itu pengemudi mungkin melakukan apa saja — mungkin secara proaktif mentransfer tekstur ke VRAM, atau mungkin tidak repot mengunggah tekstur sampai Anda mencoba membuat render menggunakannya untuk pertama kali.

Anda mungkin atau mungkin tidak dapat melakukan sesuatu tentang itu, tetapi Anda dapat membuat perkiraan jumlah waktu yang diperlukan untuk melakukan panggilan "buat tekstur". Tentu saja, semua angka akan berubah tergantung pada perangkat keras dan perangkat lunaknya, jadi mungkin tidak layak menghabiskan banyak waktu untuk merekayasa ulang angka-angka tersebut. Jadi coba saja dan lihat! Pilih metrik: "jumlah tekstur per bingkai" atau "ukuran total tekstur per bingkai", pilih kuota (katakanlah, 4 tekstur per bingkai), dan mulai uji stres itu.

Dalam kasus patologis, Anda bahkan mungkin perlu melacak kedua kuota secara bersamaan (mis. Batas hingga 4 tekstur per frame atau 2 MB tekstur per frame, mana yang lebih rendah). Tetapi trik sebenarnya untuk sebagian besar aliran tekstur adalah mencari tahu tekstur mana yang ingin Anda masukkan ke dalam memori Anda yang terbatas, bukan berapa banyak waktu yang diperlukan untuk menyalinnya.

Juga, kasus-kasus patologis untuk pembuatan tekstur — seperti banyak tekstur kecil yang dibutuhkan sekaligus — cenderung menjadi kasus-kasus patologis untuk area lain juga. Layak untuk mendapatkan implementasi kerja yang sederhana sebelum mengkhawatirkan berapa banyak mikrodetik yang dibutuhkan untuk menyalin suatu tekstur. (Plus, hit performa nyata mungkin tidak terjadi karena waktu CPU pada panggilan "buat tekstur", tetapi sebagai waktu GPU pada frame pertama Anda menggunakan tekstur.)

John Calsbeek
sumber
Itu penjelasan yang cukup bagus. Banyak hal yang tidak saya ketahui tetapi masuk akal. Alih-alih mengujinya, saya akan mengukur overhead pembuatan tekstur dalam runtime, mulai dengan lembut dan cepat katakan, 80% dari waktu eksekusi yang tersedia untuk memberikan ruang bagi pencilan.
Panda Pajama
@ Pamanda Saya agak skeptis tentang itu. Saya berharap kondisi mapan adalah "tidak ada tekstur yang disalin," dan sejumlah besar variasi. Dan seperti yang saya katakan, saya menduga bahwa bagian dari hit adalah frame render pertama yang menggunakan tekstur, yang jauh lebih sulit untuk diukur secara dinamis tanpa mempengaruhi kinerja.
John Calsbeek
Juga, inilah presentasi NVIDIA tentang transfer tekstur yang tidak sinkron. Hal utama yang ia bawa pulang, sejauh yang saya baca, adalah menggunakan tekstur terlalu cepat setelah Anda mengunggahnya akan macet. developer.download.nvidia.com/GTC/PDF/GTC2012/PresentationPDF/…
John Calsbeek
Saya bukan pengemudi dev jockey, tetapi apakah itu biasa? Tidak terlalu masuk akal untuk mengimplementasikan driver dengan cara itu, karena tekstur penggunaan pertama sangat mungkin terjadi pada paku (seperti pada awal setiap level) daripada ditempatkan di sepanjang timeline.
Panda Pajama
@PandaPajama Ini juga umum untuk aplikasi untuk membuat lebih banyak tekstur daripada VRAM yang tersedia, dan untuk membuat tekstur dan kemudian tidak pernah menggunakannya. Kasus yang umum adalah "menciptakan sekelompok tekstur dan kemudian segera menarik sebuah adegan yang menggunakan mereka," dalam hal menjadi malas membantu pengemudi, karena dapat mengetahui mana tekstur benar-benar digunakan, dan bahwa frame pertama akan halangan pula . Tapi saya juga bukan sopir dev, bawa dengan sebutir garam (dan coba!).
John Calsbeek