Three.js: Pemuat aset non-pemblokiran

Dibuat pada 11 Jul 2017  ·  53Komentar  ·  Sumber: mrdoob/three.js

Seperti yang dibahas di https://github.com/mrdoob/three.js/issues/11301 salah satu masalah utama yang kami miliki di WebVR, meskipun mengganggu pengalaman non-VR juga, adalah memblokir utas utama saat memuat aset.

Dengan implementasi baru-baru ini pada traversal tautan di browser, pemuatan tanpa pemblokiran adalah suatu keharusan untuk memastikan pengalaman pengguna yang memuaskan. Jika Anda melompat dari satu halaman ke halaman lain dan halaman target mulai memuat aset yang memblokir mainthread, itu akan memblokir fungsi render sehingga tidak ada bingkai yang dikirimkan ke headset dan setelah beberapa saat, browser akan mengeluarkan kita dari VR dan itu akan mengharuskan pengguna untuk mengeluarkan headset, klik enter VR lagi (gerakan pengguna diperlukan untuk melakukannya) dan kembali ke pengalaman.

Saat ini kita dapat melihat dua implementasi pemuatan file OBJ yang tidak memblokir:

  • (1) Menggunakan webworkers untuk mengurai file obj dan kemudian mengembalikan data kembali ke utas utama WWOBJLoader :
    Di sini penguraian dilakukan secara bersamaan dan Anda dapat memiliki beberapa pekerja secara bersamaan. Kelemahan utama adalah bahwa setelah Anda memuat data, Anda perlu mengirim payload kembali ke mainthread untuk merekonstruksi instance TIGA objek dan bagian itu dapat memblokir utas utama:
    https://github.com/kaisalmen/WWOBJLoader/blob/master/src/loaders/WWOBJLoader2.js#L312 -L423
  • (2) Janji Mainthread dengan parsing yang ditangguhkan menggunakan setTimeOut: Oculus ReactVR : Loader ini terus membaca baris menggunakan slot waktu kecil untuk mencegah pemblokiran utas utama dengan memanggil setTimeout: https://github.com/facebook/react-vr/blob/master /ReactVR/js/Loaders/WavefrontOBJ/OBJParser.js#L281 -L298
    Dengan pendekatan ini, pemuatan akan lebih lambat karena kami hanya menguraikan beberapa baris pada setiap slot waktu, tetapi keuntungannya adalah setelah penguraian selesai, kami akan memiliki TIGA objek yang siap digunakan tanpa biaya tambahan.

Keduanya memiliki pro dan kontra dan sejujurnya saya bukan ahli webworkers untuk mengevaluasi implementasi itu tetapi Ini adalah diskusi menarik yang idealnya akan mengarah ke modul generik yang dapat digunakan untuk mem-port loader ke versi non-blocking.

Ada saran?

/cc @mikearmstrong001 @kaisalmen @delapuente @spite

Suggestion

Komentar yang paling membantu

Rilis proposal pertama dari THREE.UpdatableTexture

Idealnya itu harus menjadi bagian dari TIGA.Tekstur apa pun, tetapi saya akan mengeksplorasi pendekatan ini terlebih dahulu.

Semua 53 komentar

Anda dapat memiliki pemuat berbasis Janji+pekerja+tambahan (sedikit seperti campuran kedua poin)

Berikan URL sumber ke skrip pekerja, Ambil sumber daya, kembalikan struct objek yang dapat ditransfer dengan buffer, struct, bahkan ImageBitmaps yang diperlukan; itu harus cukup mudah untuk tidak membutuhkan banyak overhead pemrosesan three.js.

Data unggahan ke GPU akan diblokir, tetapi Anda dapat membuat antrean untuk mendistribusikan perintah di berbagai bingkai, melalui display.rAF. Perintah dapat dijalankan satu per satu per frame, atau menghitung waktu rata-rata operasi dan menjalankan sebanyak yang "aman" untuk dijalankan di frame budge saat ini (sesuatu yang mirip dengan requestIdleCallback akan menyenangkan, tetapi tidak didukung secara luas , dan ini bermasalah dalam sesi WebVR). Juga dapat ditingkatkan dengan menggunakan bufferSubData, texSubImage2D, dll.

Dukungan untuk pekerja dan objek yang dapat ditransfer cukup solid saat ini, khususnya di browser yang mendukung WebVR.

Hai semua, saya memiliki prototipe yang tersedia yang mungkin menarik bagi Anda dalam konteks ini. Lihat cabang berikut:
https://github.com/kaisalmen/WWOBJLoader/tree/Commons
Di sini bagian penyediaan mesh telah sepenuhnya dipisahkan dari WWOBJLoader2 :
https://github.com/kaisalmen/WWOBJLoader/blob/Commons/src/loaders/WWLoaderCommons.js

WWLoaderCommons memudahkan untuk mengimplementasikan penyedia mesh lain (pemuat format file). Pada dasarnya, ini mendefinisikan bagaimana implementasi pekerja web harus menyediakan data mesh kembali ke utas utama dan memproses/mengintegrasikannya ke dalam adegan. Lihat penyedia sampah segitiga acak yang berfungsi sebagai demonstran teknologi:
https://github.com/kaisalmen/WWOBJLoader/tree/Commons/test/meshspray
https://kaisalmen.de/proto/test/meshspray/main.src.html

Bahkan dalam implementasi saat ini WWOBJLoader2 bergantung pada objek yang dapat ditransfer (ArrayBuffers/ByteBuffers) untuk menyediakan data mentah BufferedGeometry untuk Mesh dari pekerja ke utas utama. Dari segi waktu, pembuatan Mesh dari ByteBuffers yang disediakan dapat diabaikan. Setiap kali mesh yang lebih besar diintegrasikan ke dalam adegan namun rendering terhenti (salinan data, penyesuaian grafik adegan ... !?). Ini selalu terjadi terlepas dari sumbernya (koreksi saya jika saya salah).
Mode "aliran" dari WWOBJLoader2 menghaluskan kios-kios ini, tetapi jika satu bagian mesh dari model OBJ Anda berbobot 0,5 juta simpul, maka rendering akan berhenti untuk jangka waktu yang lebih lama.

Saya telah membuka edisi baru untuk merinci apa yang sebenarnya telah saya lakukan di cabang yang dipesan lebih dahulu dan mengapa:
https://github.com/kaisalmen/WWOBJLoader/issues/11
Masalah ini masih berupa rintisan dan detailnya akan segera menyusul.

Untuk menawarkan beberapa angka, berikut adalah profil kinerja https://threejs.org/examples/webgl_loader_gltf2.html , memuat model 13MB dengan tekstur 2048x2048.

screen shot 2017-07-11 at 10 11 42 pm

Dalam hal ini hal utama yang memblokir utas utama adalah mengunggah tekstur ke GPU, dan sejauh yang saya tahu itu tidak dapat dilakukan dari WW.. baik pemuat harus menambahkan tekstur secara bertahap, atau three.js harus menanganinya secara internal.

Bagi yang penasaran, potongan terakhir yang memblokir utas utama adalah penambahan cubemap lingkungan.

Tujuan utama dari react-vr tidak harus memiliki loader paling optimal dalam hal waktu jam dinding tetapi tidak menyebabkan frame out yang tiba-tiba dan tidak terduga saat memuat konten baru. Apa pun yang dapat kita lakukan untuk meminimalkan ini bermanfaat bagi semua kecuali VR.

Tekstur jelas merupakan masalah dan langkah pertama yang jelas adalah secara opsional memuatnya secara bertahap - satu set garis sekaligus untuk tekstur besar. Karena unggahan disembunyikan untuk program klien, akan sulit bagi mereka untuk mengelolanya, tetapi saya akan melakukannya karena ini diekspos lebih terbuka ke perender webgl untuk menghilangkan tekanan three.js

Untuk penguraian gltf, saya biasanya melihat pemblokiran 500 ms pada pengujian saya, ini signifikan dan saya lebih suka pendekatan inkremental untuk semua pemuat (yang juga harus dapat dikloning)

Premis React VR adalah untuk mendorong konten dinamis yang mudah didorong oleh gaya web untuk mendorong lebih banyak pengembang, dan ini akan mendorong lebih banyak penekanan pada peningkatan penanganan dinamis. Sebagian besar waktu kita tidak tahu aset mana yang akan diperlukan di awal aplikasi yang dibuat pengguna.

@kaisalmen Terima kasih atas tautannya

Di Elation Engine / JanusWeb, kami sebenarnya melakukan semua penguraian model kami menggunakan kumpulan utas pekerja, yang bekerja dengan cukup baik. Setelah pekerja selesai memuat setiap model, kami membuat serial menggunakan object.toJSON() , mengirimkannya ke utas utama dengan postMessage() , dan kemudian memuatnya menggunakan ObjectLoader.parse() . Ini menghapus sebagian besar bagian pemblokiran dari kode loader - masih ada beberapa waktu yang dihabiskan di ObjectLoader.parse() yang mungkin dapat dioptimalkan, tetapi keseluruhan interaktivitas dan kecepatan pemuatan meningkat secara drastis. Karena kami menggunakan kumpulan pekerja, kami juga dapat mengurai beberapa model secara paralel, yang merupakan kemenangan besar dalam adegan yang kompleks.

Di sisi tekstur, ya, saya pikir beberapa perubahan diperlukan untuk fungsionalitas pengunggahan tekstur three.js. Pengunggah yang dipotong menggunakan texSubImage2D akan ideal, kemudian kita dapat melakukan pembaruan sebagian tekstur besar pada beberapa bingkai, seperti yang disebutkan di atas.

Saya akan sangat senang untuk berkolaborasi dalam perubahan ini, karena akan menguntungkan banyak proyek yang menggunakan Three.js sebagai basis

Saya pikir menggunakan texSubImage2D adalah ide yang bagus.
Tetapi juga mengapa WebGL tidak mengunggah tekstur secara asinkron.
OpenGL dan lib lain memiliki batasan yang sama?

Dan hal lain yang saya pikirkan adalah kompilasi GLSL.
Apakah itu akan menjatuhkan bingkai? Atau cukup cepat dan kita tidak perlu peduli?

Ya, ini juga merupakan masalah dalam OpenGL asli - mengkompilasi shader dan mengunggah data gambar adalah operasi sinkron/pemblokiran. Inilah sebabnya mengapa sebagian besar mesin game merekomendasikan atau bahkan memaksa Anda untuk memuat semua konten sebelum Anda memulai level - umumnya dianggap terlalu banyak kinerja yang baik untuk memuat sumber daya baru bahkan dari hard drive, dan di sini kami mencoba melakukannya secara asinkron melalui internet...kami sebenarnya memiliki masalah yang lebih sulit daripada kebanyakan pengembang game, dan kami harus menggunakan teknik yang lebih canggih jika kami ingin dapat mengalirkan konten baru dengan cepat.

Mengunggah tekstur tidak akan terlalu bermasalah jika kita menggunakan API ImageBitmap di masa mendatang. Lihat https://youtu.be/wkDd-x0EkFU?t=82 .

BTW: Terima kasih kepada @spite , kami sudah memiliki ImageBitmapLoader eksperimental dalam proyek.

@ Mugen87 sebenarnya saya sudah melakukan semua beban tekstur saya dengan ImageBitmap di Elation Engine / JanusWeb - itu pasti membantu dan layak untuk diintegrasikan ke dalam inti Three.js, tetapi ada dua biaya utama yang terkait dengan penggunaan tekstur di WebGL - waktu dekode gambar , dan waktu unggah gambar - ImageBitmap hanya membantu dengan yang pertama.

Ini memangkas waktu pemblokiran CPU sekitar 50% dalam pengujian saya, tetapi mengunggah tekstur besar ke GPU, terutama 2048x2048 dan lebih tinggi, dapat dengan mudah memakan waktu satu detik atau lebih.

Akan lebih mudah untuk mencoba apa yang disarankan @jbaicoianu . Bagaimanapun, jika memilih alternatif utas utama, ini tampaknya sangat cocok untuk requestIdleCallback alih-alih setTimeout.

Saya setuju dengan Anda semua, saya percaya pendekatan untuk memuat dan mengurai semua yang ada pada pekerja, membuat objek yang diperlukan kembali di utas utama (jika sangat mahal dapat dilakukan dalam beberapa langkah) dan kemudian menyertakan pemuatan tambahan pada penyaji.
Untuk MVP, kita dapat mendefinisikan maxTexturesUploadPerFrame (secara default tak terbatas), dan render akan menangani pemuatan dari kumpulan sesuai dengan nomor itu.
Dalam iterasi berikut kita dapat menambahkan logika, seperti komentar @spite , untuk mengukur rata-rata dan secara otomatis mengunggahnya berdasarkan rentang waktu yang aman sebelum memblokir. Ini dapat dilakukan pada awalnya untuk setiap tekstur sebagai satu unit, tetapi kemudian dapat ditingkatkan untuk mengunggah potongan secara bertahap untuk tekstur yang lebih besar.

requestIdleCallback akan menyenangkan, tetapi tidak didukung secara luas, dan ini bermasalah dalam sesi WebVR

@meskipun saya ingin tahu tentang kalimat Anda, apa yang Anda maksud dengan bermasalah?

Saya memiliki THREE.UpdatableTexture untuk memperbarui tekstur secara bertahap menggunakan texSubImage2D, tetapi perlu sedikit penyesuaian three.js. Idenya adalah untuk mempersiapkan PR untuk menambah dukungan.

Mengenai requestIdleCallback (rIC):

  • pertama, ini didukung di Chrome dan Firefox, dan meskipun dapat di-polyfill dengan mudah, versi poly-fill mungkin sedikit mengurangi tujuannya.

  • kedua: dengan cara yang sama vrDisplay.requestAnimationFrame (rAF) perlu dipanggil alih-alih window.rAF saat menyajikan, hal yang sama berlaku untuk rIC, seperti yang dibahas dalam crbug ini . Itu berarti bahwa loader perlu mengetahui tampilan aktif saat ini setiap saat, atau itu akan berhenti menyala tergantung pada apa yang ditampilkan. Ini tidak terlalu rumit, itu hanya menambah lebih banyak kerumitan pada pengkabelan loader (yang idealnya hanya melakukan pekerjaan mereka, terlepas dari status presentasi). Pilihan lain adalah memiliki bagian dalam threejs yang menjalankan pekerjaan tambahan di utas utama untuk membagikan tampilan saat ini; saya pikir itu jauh lebih mudah dilakukan sekarang dengan perubahan terbaru ke VR di threejs.

Pertimbangan lain: agar dapat mengunggah satu tekstur besar dalam beberapa langkah menggunakan texSubImage2D (256x256 atau 512x512), kita memerlukan konteks WebGL2 untuk memiliki fitur offset dan kliping. Jika tidak, gambar harus dipotong terlebih dahulu melalui kanvas, pada dasarnya dipasang di sisi klien sebelum diunggah.

@meskipun Poin bagus, saya tidak berpikir tentang rIC tidak dipanggil saat presentasi, pada awalnya saya pikir kita harus membutuhkan display.rIC tapi saya percaya bahwa .rIC harus dilampirkan ke jendela dan dipanggil ketika jendela atau layar keduanya menganggur.
Saya yakin saya tidak mendengar apa pun yang terkait dengan ini dalam diskusi spesifikasi webvr @kearwood mungkin memiliki informasi lebih lanjut, tetapi jelas merupakan masalah yang harus kita atasi.

Menantikan untuk melihat PR UpdateableTexture Anda! :) Bahkan jika itu hanya WIP kita bisa memindahkan beberapa diskusi di sana.

Mungkin loader bisa menjadi seperti ini...

THREE.MyLoader = ( function () {

    // parse file and output js object
    function parser( text ) {
        return { 'vertices': new Float32Array() }
    }

    // convert js object to THREE objects.
    function builder( data ) {
        var geometry = new THREE.BufferGeometry();
        geometry.addAttribute( new THREE.BufferAttribute( data.vertices, 3 );
        return geometry;
    }

    function MyLoader( manager ) {}
    MyLoader.prototype = {
        constructor: MyLoader,
        load: function ( url, onLoad, onProgress, onError  ) {},
        parse: function ( text ) {
            return builder( parser( text ) );
        },
        parseAsync: function ( text, onParse ) {
            var code = parser.toString() + '\nonmessage = function ( e ) { postMessage( parser( e.data ) ); }';
            var blob = new Blob( [ code ], { type: 'text/plain' } );
            var worker = new Worker( window.URL.createObjectURL( blob ) );
            worker.addEventListener( 'message', function ( e ) {
                onParse( builder( e.data ) );
            } );
            worker.postMessage( text );
        }
    }
} )();

Rilis proposal pertama dari THREE.UpdatableTexture

Idealnya itu harus menjadi bagian dari TIGA.Tekstur apa pun, tetapi saya akan mengeksplorasi pendekatan ini terlebih dahulu.

@mrdoob saya melihat manfaat memiliki kode yang sama persis disalurkan ke pekerja, rasanya sangat salah . Saya ingin tahu apa dampak dari serialisasi, penggumpalan, dan evaluasi ulang skrip; tidak ada yang terlalu buruk, tetapi saya tidak berpikir browser dioptimalkan untuk kebiasaan ini

Juga, idealnya pengambilan sumber daya itu sendiri akan terjadi pada pekerja. Dan saya pikir metode parser() di browser akan membutuhkan importScripts dari three.js itu sendiri.

Tetapi satu poin untuk mendefinisikan pemuat sinkronisasi/asinkron akan sangat membantu!

@mrdoob fungsi builder dapat sepenuhnya generik dan umum untuk semua loader (WIP: https://github.com/kaisalmen/WWOBJLoader/blob/Commons/src/loaders/support/WWMeshProvider.js#LL215- LL367; Pembaruan: belum diisolasi dalam suatu fungsi). Jika data input dibatasi ke objek js murni tanpa referensi ke objek THREE (itu yang ada dalam pikiran Anda, bukan?) kita bisa membuat kode pekerja serial tanpa perlu mengimpor pekerja (apa WWOBJLoader tidak). Ini mudah untuk Geometri, tetapi Material/Shaders (jika didefinisikan dalam file) hanya dapat dibuat di pembuat dan hanya dijelaskan sebagai JSON sebelumnya oleh parser .
Seorang pekerja harus memberi sinyal pada setiap Mesh baru dan penyelesaiannya, saya pikir. Itu bisa diubah seperti ini:

// parse file and output js object
function parser( text, onMeshLoaded, onComplete ) {
    ....
}
parse: function ( text ) {
    var node = new THREE.Object3d();
    var onMeshLoaded = function ( data ) {
        node.add( builder( data ) );
    };

    // onComplete as second callbackonly provided in async case
    parser( text, onMeshLoaded ) );
    return node;
},

Utilitas pembangun pekerja sangat membantu + beberapa protokol komunikasi umum yang tidak bertentangan dengan ide Anda untuk menggunakan parser apa adanya, tetapi perlu beberapa pembungkus, saya pikir. Keadaan saat ini tentang evolusi WWOBJLoader: https://github.com/kaisalmen/WWOBJLoader/blob/Commons/src/loaders/support/WWMeshProvider.js#LL40 -LL133, sedangkan panggilan front-end adalah report_progress, meshData, dan selesai.

Pembaruan2:

  • Apakah menurut Anda Parser harus tanpa kewarganegaraan? Tidak apa-apa untuk builder , tetapi masuk akal untuk dapat mengatur beberapa parameter untuk menyesuaikan perilaku parser . Ini juga menyiratkan parameter konfigurasi harus dapat ditransfer ke pekerja yang tidak tergantung pada penguraian
  • Akan keren untuk memiliki sesuatu seperti fungsi run yang memakan objek konfigurasi umum. Seorang direktur generik kemudian dapat memberi makan setiap loader dengan instruksi (ini sekarang bekerja di cabang Commons yang dipesan lebih dahulu dari WWOBJLoader , btw)
  • dev yang sedang berlangsung: WWOBJLoader2 sekarang meluas OBJLoader dan menimpa parse. Jadi, kami memiliki kedua parsing caps, tetapi di kelas yang berbeda. Itu mendekati proposal, tetapi belum sejalan. Beberapa kode parser perlu disatukan dan pada akhirnya kedua kelas perlu digabungkan

Itu saja untuk saat ini. Umpan balik diterima

@mrdoob Saya suka ide menyusun pekerja dari kode loader dengan cepat. Pendekatan saya saat ini hanya memuat seluruh js aplikasi gabungan dan hanya menggunakan titik masuk yang berbeda dari utas utama, jelas tidak seefisien membuat pekerja hanya dengan kode yang mereka butuhkan.

Saya suka pendekatan menggunakan format transmisi yang dipangkas untuk melewati antar pekerja, karena mudah untuk menandai TypedArrays tersebut sebagai dapat ditransfer saat meneruskan kembali ke utas utama. Dalam pendekatan saya saat ini, saya menggunakan metode .toJSON() pada pekerja, tetapi kemudian saya memeriksa dan mengganti array JS untuk simpul, UV, dll. dengan tipe TypedArray yang sesuai, dan menandainya sebagai dapat ditransfer saat menelepon pascaPesan. Ini membuat penggunaan parsing/memori sedikit lebih ringan di utas utama, dengan biaya sedikit lebih banyak pemrosesan/penggunaan memori di pekerja - ini adalah pertukaran yang bagus untuk dilakukan, tetapi itu bisa dibuat lebih efisien dengan memperkenalkan a format transmisi baru seperti yang Anda usulkan, atau dengan memodifikasi .toJSON() untuk secara opsional memberi kami TypedArrays alih-alih array JS.

Dua kelemahan yang saya lihat dari pendekatan yang disederhanakan ini adalah:

  • Perlu penulisan ulang model loader yang ada. Banyak loader menggunakan TIGA kelas dan fungsi dengan ruang nama bawaan untuk melakukan tugasnya, jadi kita mungkin harus memasukkan beberapa set minimal kode three.js - dan dengan pekerja ini menjadi rumit
  • Format transmisi harus menangkap hierarki objek dengan benar, serta jenis objek yang berbeda (Mesh, SkinnedMesh, Light, Camera, Object3D, Line, dll.)

@spite Mengenai "Juga, idealnya pengambilan sumber daya itu sendiri akan terjadi pada pekerja." - ini adalah pemikiran saya ketika saya pertama kali menerapkan pemuat aset berbasis pekerja untuk Elation Engine - Saya memiliki kumpulan 4 atau 8 pekerja, dan saya akan memberikan mereka pekerjaan saat tersedia, dan kemudian pekerja akan mengambil file, mengurai mereka, dan mengembalikannya ke utas utama. Namun, dalam praktiknya, ini berarti bahwa unduhan akan memblokir penguraian, dan Anda akan kehilangan manfaat yang Anda dapatkan dari pipelining, dll. jika Anda meminta semuanya sekaligus.

Setelah kami menyadari hal ini, kami menambahkan lapisan lain untuk mengelola semua unduhan aset kami, dan kemudian pengunduh aset mengaktifkan peristiwa untuk memberi tahu kami saat aset tersedia. Kami kemudian meneruskan ini ke kumpulan pekerja, menggunakan yang dapat ditransfer pada data file biner untuk memasukkannya ke pekerja secara efisien. Dengan perubahan ini, semua unduhan terjadi lebih cepat meskipun mereka berada di utas utama, dan parser dapat menjalankan pemrosesan penuh, daripada memutar-mutar ibu jari mereka menunggu data. Secara keseluruhan, ini menjadi salah satu pengoptimalan terbaik yang kami buat dalam hal kecepatan pemuatan aset.

Pada topik pemuatan tekstur, saya telah membuat bukti konsep kelas FramebufferTexture , yang dilengkapi dengan pendamping FramebufferTextureLoader . Jenis tekstur ini meluas WebGLRenderTarget , dan pemuatnya dapat dikonfigurasi untuk memuat tekstur dalam ubin potongan dengan ukuran tertentu, dan menyusunnya ke dalam framebuffer menggunakan requestIdleCallback() .

https://baicoianu.com/~bai/three.js/examples/webgl_texture_framebuffer.html

Dalam contoh ini, cukup pilih ukuran gambar dan ukuran ubin dan itu akan memulai proses pemuatan. Pertama kita menginisialisasi tekstur menjadi merah murni. Kami mulai mengunduh gambar (sekitar 10mb, jadi berikan sedikit), dan ketika mereka selesai, kami mengubah latar belakang menjadi biru. Pada titik ini kita mulai mem-parsing gambar dengan createImageBitmap() untuk mem-parsing file, dan setelah selesai kita menyiapkan sejumlah callback menganggur yang berisi panggilan lebih lanjut ke createImageBitmap() yang secara efisien membagi gambar menjadi ubin . Ubin ini dirender ke dalam framebuffer pada sejumlah bingkai, dan memiliki dampak yang jauh lebih rendah pada waktu bingkai daripada melakukannya sekaligus.

CATATAN - FireFox saat ini tampaknya tidak mengimplementasikan semua versi createImageBitmap , dan saat ini membuat kesalahan bagi saya ketika mencoba membagi menjadi ubin. Akibatnya, demo ini saat ini hanya berfungsi di Chrome. Adakah yang punya referensi untuk peta jalan dukungan createImageBitmap di FireFox?

Ada beberapa pembersihan yang perlu saya lakukan, prototipe ini agak berantakan, tetapi saya sangat senang dengan hasilnya dan setelah saya dapat menemukan cara untuk mengatasi masalah lintas-browser (canvas fallback, dll), saya mempertimbangkan untuk menggunakan ini sebagai default untuk semua tekstur di JanusWeb. Efek fade-in agak rapi juga, dan kita bahkan bisa mendapatkan versi yang lebih mewah dan blit terlebih dahulu, kemudian secara progresif memuat ubin dengan detail yang lebih tinggi.

Apakah ada alasan terkait kinerja atau fitur yang dapat dipikirkan siapa pun mengapa mungkin merupakan ide yang buruk untuk memiliki framebuffer untuk setiap tekstur dalam adegan, yang bertentangan dengan referensi tekstur standar? Saya tidak dapat menemukan apa pun tentang max. framebuffers per adegan, sejauh yang saya tahu setelah framebuffer telah diatur, jika Anda tidak merendernya maka itu sama dengan referensi tekstur lainnya, tetapi saya merasa seperti saya kehilangan sesuatu yang jelas untuk mengapa ini akan menjadi ide yang sangat buruk :)

@jbaicoianu re: firefox's createImageBitmap, alasannya adalah mereka tidak mendukung parameter kamus, sehingga tidak mendukung orientasi gambar atau konversi ruang warna. itu membuat sebagian besar aplikasi API sangat tidak berguna. Saya mengajukan dua bug yang terkait dengan masalah ini: https://bugzilla.mozilla.org/show_bug.cgi?id=1367251 dan https://bugzilla.mozilla.org/show_bug.cgi?id=1335594

@meskipun itulah yang saya pikirkan juga, saya telah melihat bug ini tentang tidak mendukung kamus opsi - tetapi dalam hal ini saya bahkan tidak menggunakannya, saya hanya mencoba menggunakan opsi x, y, w, h. Kesalahan spesifik yang saya dapatkan adalah:

Argument 4 of Window.createImageBitmap '1024' is not a valid value for enumeration ImageBitmapFormat.

Yang membingungkan, karena saya tidak melihat versi createImageBitmap dalam spesifikasi yang menggunakan ImageBitmapFormat sebagai argumen.

Apakah ada alasan terkait kinerja atau fitur yang dapat dipikirkan siapa pun mengapa mungkin merupakan ide yang buruk untuk memiliki framebuffer untuk setiap tekstur dalam adegan, yang bertentangan dengan referensi tekstur standar? Saya tidak dapat menemukan apa pun tentang max. framebuffers per adegan, sejauh yang saya tahu setelah framebuffer telah diatur, jika Anda tidak merendernya maka itu sama dengan referensi tekstur lainnya, tetapi saya merasa seperti saya kehilangan sesuatu yang jelas untuk mengapa ini akan menjadi ide yang sangat buruk :)

@jbaicoianu THREE.WebGLRenderTarget menyimpan framebuffer, tekstur, dan buffer render. Saat tekstur sudah terpasang, Anda dapat menghapus framebuffer dan buffer render dan hanya mempertahankan teksturnya. Sesuatu seperti ini harus melakukan ini (tidak diuji):

texture = target.texture;
target.texture = null; // so the webgl texture is not deleted by dispose()
target.dispose();

@wrr itu bagus untuk diketahui, terima kasih. Saya pasti harus melakukan efisiensi memori dalam hal ini juga - itu pasti macet di beberapa titik jika Anda cukup mengubah parameter, jadi saya tahu ada beberapa pembersihan yang belum saya lakukan. Petunjuk lain seperti ini akan sangat dihargai.

@mrdoob dan @jbaicoianu Saya lupa menyebutkan bahwa saya juga menyukai ide itu. 😄
Saya telah merapikan kode (init yang dikerjakan ulang, objek instruksi pekerja, penanganan multi-callback yang diganti sampah, deskripsi sumber daya umum, dll.) dari OBJLoader dan WWOBJLoader dan semua contoh ( code ). Kedua loader sekarang siap untuk digabungkan. Mereka akan sesuai dengan cetak biru Anda semoga beberapa waktu minggu depan tergantung pada waktu luang saya:
Tes WWOBJLoader2 yang diarahkan:
https://kaisalmen.de/proto/test/wwparallels/main.src.html
Pengguna yang diarahkan dari WorkerSupport generik :
https://kaisalmen.de/proto/test/meshspray/main.src.html
Tes file OBJ zip besar:
https://kaisalmen.de/proto/test/wwobjloader2stage/main.src.html

Saya akan memperbarui contoh di atas dengan kode yang lebih baru bila tersedia dan memberi tahu Anda.
Pembaruan 30-07-2017: OBJLoader2 dan WWOBJLoader2 sekarang menggunakan Parser yang identik. Mereka meneruskan data ke fungsi pembangun umum secara langsung atau dari pekerja.
Pembaruan 31-07-2017: WWOBJLoader2 hilang. OBJLoader2 menyediakan parse dan parseAsync , load dan run (umpan oleh LoaderDirector atau secara manual)

Pembaruan 2017-08-09:
Memindahkan pembaruan ke pos baru.

OBJLoader2 adalah tanda tangan dan perilaku yang kompatibel lagi dengan OBJLoader (Saya memecahkan ini selama evolusi), OBJLoader2 menyediakan parseAsync dan load dengan useAsync bendera sebagai tambahan. Saya pikir, sekarang sudah siap disebut V2.0.0-Beta. Di sini Anda menemukan status dev saat ini:
https://github.com/kaisalmen/WWOBJLoader/tree/V2.0.0-Beta/src/loaders

Saya telah mengekstrak kelas LoaderSupport (terlepas dari OBJ) yang berfungsi sebagai utilitas dan alat pendukung yang diperlukan. Mereka dapat digunakan kembali untuk pemuat berbasis pekerja potensial lainnya. Semua kode di bawah ini, saya taruh di bawah namespace THREE.LoaderSupport untuk menonjolkan ketergantungannya dari OBJLoader2 :

  • Builder : Untuk bangunan jala umum
  • WorkerDirector : Membuat loader melalui refleksi, memproses PrepData dalam antrian dengan jumlah pekerja yang dikonfigurasi. Digunakan untuk mengotomatisasi loader sepenuhnya (demo MeshSpray dan Parallels)
  • WorkerSupport : Kelas utilitas untuk membuat pekerja dari kode yang ada dan membuat protokol komunikasi sederhana
  • PrepData + ResourceDescriptor : Deskripsi digunakan untuk otomatisasi atau hanya untuk deskripsi terpadu di antara contoh
  • Commons : Kemungkinan kelas dasar untuk loader (menggabungkan parameter umum)
  • Callbacks : (onProgress, onMeshAlter, onLoad) digunakan untuk otomatisasi dan arah dan LoadedMeshUserOverride digunakan untuk memberikan info kembali dari onMeshAlter (penambahan normal pada pengujian objloader2 di bawah)
  • Validator : pemeriksaan variabel nol/tidak terdefinisi

@mrdoob @jbaicoianu OBJLoader2 sekarang membungkus parser seperti yang disarankan (ini dikonfigurasi dengan parameter yang disetel secara global atau diterima oleh PrepData untuk dijalankan). Builder menerima setiap jala mentah dan parser mengembalikan simpul dasar, tetapi selain itu cocok dengan cetak biru.
Masih ada beberapa kode pembantu di OBJLoader2 untuk serialisasi Parser yang mungkin tidak diperlukan.
Pembangun perlu dibersihkan karena objek kontrak/parameter untuk fungsi buildMeshes masih sangat dipengaruhi oleh pemuatan OBJ dan oleh karena itu masih dianggap dalam konstruksi.

Kode perlu dipoles, tetapi kemudian siap untuk umpan balik, diskusi, kritik, dll...

Contoh dan Tes

OBJ Loader menggunakan run dan load:
https://kaisalmen.de/proto/test/objloader2/main.src.html
OBJ Loader menggunakan run async dan parseAsync:
https://kaisalmen.de/proto/test/wwobjloader2/main.src.html
Penggunaan yang diarahkan dari run async OBJLoader2:
https://kaisalmen.de/proto/test/wwparallels/main.src.html
Penggunaan WorkerSupport generik yang diarahkan:
https://kaisalmen.de/proto/test/meshspray/main.src.html
Tes file OBJ zip besar:
https://kaisalmen.de/proto/test/wwobjloader2stage/main.src.html

Terlihat bagus! Apakah Anda mengetahui perubahan ini di OBJLoader ? #11871 565c6fd0f3d9b146b9434e5fccfa2345a90a3842

Ya, saya perlu port ini. Saya mengusulkan beberapa pengukuran kinerja yang dapat diproduksi ulang. Akan mulai bekerja pada keduanya akhir pekan ini. Kapan Anda berencana untuk merilis r87? Dukungan N-gon bisa membuatnya tergantung pada tanggal.

@mrdoob dan voila: https://github.com/mrdoob/three.js/pull/11928 dukungan n-gon

Pembaruan status ( kode ):
Pekerja yang dibuat sekarang dapat mengonfigurasi parser apa pun di dalam pekerja melalui parameter yang diterima oleh pesan. WorkerSupport menyediakan implementasi runner pekerja referensi ( code ) yang dapat sepenuhnya diganti dengan kode sendiri jika diinginkan atau jika diperlukan.
Pekerja akan membuat dan menjalankan parser dalam metode run dari WorkerRunnerRefImpl ( Parser tersedia di dalam lingkup pekerja; this.applyProperties memanggil setter atau properti dari pengurai):

WorkerRunnerRefImpl.prototype.run = function ( payload ) {
    if ( payload.cmd === 'run' ) {

        console.log( 'WorkerRunner: Starting Run...' );

        var callbacks = {
            callbackBuilder: function ( payload ) {
                self.postMessage( payload );
            },
            callbackProgress: function ( message ) {
                console.log( 'WorkerRunner: progress: ' + message );
            }
        };

        // Parser is expected to be named as such
        var parser = new Parser();
        this.applyProperties( parser, payload.params );
        this.applyProperties( parser, payload.materials );
        this.applyProperties( parser, callbacks );
        parser.parse( payload.buffers.input );

        console.log( 'WorkerRunner: Run complete!' );

        callbacks.callbackBuilder( {
            cmd: 'complete',
            msg: 'WorkerRunner completed run.'
        } );

    } else {

        console.error( 'WorkerRunner: Received unknown command: ' + payload.cmd );

    }
};

Pesan dari OBJLoader2.parseAsync terlihat seperti ini:

this.workerSupport.run(
    {
        cmd: 'run',
        params: {
            debug: this.debug,
            materialPerSmoothingGroup: this.materialPerSmoothingGroup
        },
        materials: {
            materialNames: this.materialNames
        },
        buffers: {
            input: content
        }
    },
    [ content.buffer ]
);

Objek pesan bergantung pada Loader, tetapi konfigurasi Parser di pekerja bersifat umum.
Kode yang digunakan oleh contoh terkait di posting sebelumnya telah diperbarui dengan kode terbaru.

Saya pikir evolusi OBJLoader2 dan ekstraksi fungsi dukungan kini telah mencapai titik di mana umpan balik Anda diperlukan. Ketika semua contoh telah di-porting dari reponya ke cabang di atas, saya akan membuka PR dengan ringkasan lengkap dan kemudian meminta umpan balik

FYI, ini adalah pekerjaan dalam proses untuk membuat ImageBitmapLoader menggunakan pekerja seperti yang dibahas di atas. Mungkin yang lebih menarik, beberapa angka sulit pada hasil: https://github.com/mrdoob/three.js/pull/12456

createImageBitmap firefox, alasannya adalah mereka tidak mendukung parameter kamus, sehingga tidak mendukung orientasi gambar atau konversi ruang warna. itu membuat sebagian besar aplikasi API sangat tidak berguna.

Ini sangat disayangkan. ️

@mrdoob Apakah Anda memiliki rencana untuk mengalihkan ImageLoader ke ImageBitmapLoader di TextureLoader karena ImageBitmap seharusnya lebih sedikit memblokir untuk mengunggah ke tekstur? createImageBitmap() tampaknya bekerja di FireFox sejauh ini jika kita hanya memberikan argumen pertama. (Mungkin kita tidak perlu melewatkan argumen kedua dan lebih banyak melalui TextureLoader ?)

return createImageBitmap( blob );

Sebenarnya penting bahwa createImageBitmap () mendukung kamus opsi. Kalau tidak, Anda tidak dapat mengubah hal-hal seperti orientasi gambar (flip-Y) atau menunjukkan alfa yang dikalikan sebelumnya. Masalahnya adalah Anda tidak dapat menggunakan WebGLRenderingContext.pixelStorei untuk ImageBitmap . Dari spek :

_Jika TexImageSource adalah ImageBitmap, maka ketiga parameter ini (UNPACK_FLIP_Y_WEBGL, UNPACK_PREMULTIPLY_ALPHA_WEBGL, UNPACK_COLORSPACE_CONVERSION_WEBGL) akan diabaikan. Sebaliknya ImageBitmapOptions yang setara harus digunakan untuk membuat ImageBitmap dengan format yang diinginkan._

Jadi saya pikir kita hanya dapat beralih ke ImageBitmapLoader jika FF mendukung kamus opsi. Selain itu, properti seperti Texture.premultiplyAlpha dan Texture.flipY tidak berfungsi dengan ImageBitmap saat ini. Maksud saya jika pengguna mengaturnya, mereka tidak akan memengaruhi tekstur berdasarkan ImageBitmap yang agak disayangkan.

Oke. Saya melewatkan spesifikasi itu.

Pentingnya kamus opsi juga dibahas di sini:

https://bugzilla.mozilla.org/show_bug.cgi?id=1335594

Bug di bugzilla (https://bugzilla.mozilla.org/show_bug.cgi?id=1367251, https://bugzilla.mozilla.org/show_bug.cgi?id=1335594) tidak tersentuh selama ... dua tahun sekarang? Saya tidak berpikir itu akan membawa mereka selama ini untuk memperbaikinya.

Jadi masalahnya adalah "secara teknis" fitur tersebut didukung di FF, tetapi dalam praktiknya tidak berguna. Untuk menggunakannya, kita dapat memiliki jalur untuk Chrome yang menggunakannya, dan jalur lain untuk browser lain yang tidak menggunakannya. Masalahnya adalah, karena Firefox memang memiliki fitur tersebut, kita harus melakukan UA sniffing, yang menyebalkan.

Solusi praktisnya adalah melakukan deteksi fitur: buat gambar 2x2 menggunakan cIB dengan bendera flip, lalu baca kembali dan pastikan nilainya benar.

Tentang bug FireFox, saya juga akan menghubungi mereka secara internal. Mari kita lihat apakah kita perlu solusi setelah kita mendengar rencana mereka.

Bug di bugzilla (https://bugzilla.mozilla.org/show_bug.cgi?id=1367251, https://bugzilla.mozilla.org/show_bug.cgi?id=1335594) tidak tersentuh selama ... dua tahun sekarang? Saya tidak berpikir itu akan membawa mereka selama ini untuk memperbaikinya.

Ya maaf untuk itu saya benar-benar tidak menindaklanjutinya untuk sementara waktu -_-

Jadi masalahnya adalah "secara teknis" fitur tersebut didukung di FF, tetapi dalam praktiknya tidak berguna. Untuk menggunakannya, kita dapat memiliki jalur untuk Chrome yang menggunakannya, dan jalur lain untuk browser lain yang tidak menggunakannya. Masalahnya adalah, karena Firefox memang memiliki fitur tersebut, kita harus melakukan UA sniffing, yang menyebalkan.

Solusi praktisnya adalah melakukan deteksi fitur: buat gambar 2x2 menggunakan cIB dengan bendera flip, lalu baca kembali dan pastikan nilainya benar.

Ya, saya setuju bahwa kedua solusi itu benar-benar payah dan kita harus mencoba menghindarinya, jadi sebelum menggali salah satu dari ini, mari kita lihat apakah kita bisa membuka blokirnya di pihak kita

Saya membuat tes kinerja pengunggahan ImageBitmap . Mengunggah tekstur dalam setiap 5 detik.

Anda dapat membandingkan Gambar Biasa vs ImageBitmap.

https://rawgit.com/takahirox/three.js/ImageBitmapTest/examples/webgl_texture_upload.html (Gambar Biasa)
https://rawgit.com/takahirox/three.js/ImageBitmapTest/examples/webgl_texture_upload.html?imagebitmap (ImageBitmap)

Di jendela saya, saya melihat

| Peramban | 8192x4096 JPG 4.4MB | 2048x2048 PNG 4.5 MB |
| ---- | ---- | ---- |
| Gambar Chrome | 500ms | 140ms |
| Gambar ChromeBitmap | 165 md | 35 md |
| Gambar FireFox | 500ms | 40ms |
| FireFox ImageBitmap | 500ms | 60 md |

( texture.generateMipmaps adalah true )

Pikiran saya

  1. Performa 3x lebih baik dengan ImageBitmap di Chrome. Peningkatan yang sangat bagus.
  2. FireFox memiliki masalah kinerja ImageBitmap sekarang? Bisakah Anda mencoba di komputer Anda juga? Mencoba di ponsel juga diterima.
  3. Bahkan dengan ImageBitmap, tekstur pengunggahan tampaknya masih terhalang untuk tekstur besar. Mungkin kita perlu teknik pengunggahan parsial secara bertahap atau sesuatu untuk non-pemblokiran.

Bahkan dengan ImageBitmap, tekstur pengunggahan tampaknya masih terhalang untuk tekstur besar. Mungkin kita perlu teknik upload parsial atau semacamnya untuk non-blocking.

Saya kira satu solusi untuk masalah ini mungkin penggunaan format kompresi tekstur dan menghindari JPG atau PNG (dan dengan demikian ImageBitmap ). Akan menarik untuk melihat beberapa data kinerja dalam konteks ini.

Ya, setuju. Tapi saya kira kita mungkin masih melihat pemblokiran untuk tekstur besar terutama pada perangkat berdaya rendah seperti seluler. Bagaimanapun, evaluasi kinerjanya terlebih dahulu.

Atau gunakan terjadwal/requestIdleCallback texSubImage2D

rIC = requestIdleCallback?

ya, saya telah membuat editan ninja

OKE. Ya setuju.

BTW, saya belum terbiasa dengan tekstur terkompresi. Biarkan saya mengkonfirmasi pemahaman saya. Kami tidak dapat menggunakan Tekstur Terkompresi dengan ImageBitmap karena compressedTexImage2D tidak menerima ImageBitmap , benar?

https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/compressedTexImage2D

Saya kembali untuk mengunjungi eksperimen TiledTextureLoader lama saya - sepertinya sekarang menyebabkan driver video saya mogok dan dimulai ulang :(

(edit: sebenarnya, sepertinya memuat tekstur terbesar (16k x 16k - https://baicoianu.com/~bai/three.js/examples/textures/dotamap1_25.jpg) langsung di chrome adalah penyebab crash. Ini dulu berfungsi dengan baik, jadi sepertinya ada regresi dalam penanganan gambar chrome)

Saya telah melakukan beberapa eksperimen menggunakan generator requestIdleCallback, ImageBitmap, dan ES6 untuk membagi tekstur besar menjadi beberapa bagian untuk diunggah ke GPU. Saya menggunakan framebuffer daripada Tekstur biasa, karena meskipun Anda menggunakan texSubimage2D untuk mengisi data gambar, Anda masih perlu mengalokasikan memori terlebih dahulu, yang memerlukan pengunggahan banyak data kosong ke GPU, sedangkan framebuffer dapat dibuat dan diinisialisasi dengan satu panggilan GL.

Repositori untuk perubahan tersebut masih tersedia di sini https://github.com/jbaicoianu/THREE.TiledTexture/

Beberapa catatan dari apa yang saya ingat dari percobaan:

  • requestIdleCallback pasti membantu mengurangi jank saat memuat tekstur, dengan mengorbankan total waktu muat yang sangat meningkat
  • Dengan sedikit kerja ekstra, ini dapat dikurangi dengan terlebih dahulu mengunggah versi tekstur yang diperkecil, lalu mengisi data resolusi penuh dengan lebih santai.
  • Generator ES6 membantu membuat kode lebih mudah dipahami, dan lebih mudah ditulis tanpa membuang memori, tetapi mungkin tidak terlalu diperlukan untuk ini

Hasil saya serupa: ada trade off antara kecepatan unggah dan jankiness. (BTW saya membuat ini https://github.com/spite/THREE.UpdatableTexture).

Saya pikir agar opsi kedua berfungsi di WebGL 1, Anda sebenarnya membutuhkan dua tekstur, atau setidaknya pengubah koordinat UV. Di WebGL 2 saya pikir lebih mudah untuk menyalin sumber yang ukurannya berbeda dari tekstur target.

Ya, dengan texSubImage2D saya pikir pengubahan ukuran semacam itu tidak mungkin, tetapi ketika menggunakan framebuffer, saya menggunakan OrthographicCamera untuk merender pesawat dengan fragmen tekstur, jadi ini hanya masalah mengubah skala bidang untuk panggilan undian itu.

Tentang masalah kinerja ImageBItmap di FireFox, saya membuka bug di bugzilla

https://bugzilla.mozilla.org/show_bug.cgi?id=1486454

Saya telah mencari untuk mencoba dan lebih memahami ketika data yang terkait dengan tekstur benar-benar dimuat ke GPU dan menemukan utas ini. Dalam kasus penggunaan khusus saya, saya TIDAK khawatir tentang pemuatan dan penguraian kode file jpeg/gif lokal ke dalam tekstur, saya hanya khawatir tentang mencoba memuat data tekstur ke GPU. Setelah membaca utas ini, saya harus mengakui bahwa saya tidak sepenuhnya yakin apakah ini membahas kedua masalah atau hanya yang pertama? Mengingat saya hanya peduli dengan yang terakhir, apakah saya perlu mencari solusi lain atau adakah sesuatu di sini yang akan membantu memaksa data tekstur dimuat ke GPU?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat