Three.js: ノンブロッキングアセットローダー

作成日 2017年07月11日  ·  53コメント  ·  ソース: mrdoob/three.js

https://github.com/mrdoob/three.js/issues/11301で説明されているように、WebVRで発生する主な問題の1つは、VR以外のエクスペリエンスでも厄介ですが、アセットの読み込み中にメインスレッドをブロックすることです。

ブラウザでのリンクトラバーサルの最近の実装では、満足のいくユーザーエクスペリエンスを確保するために、非ブロッキング読み込みが必須です。 あるページから別のページにジャンプし、ターゲットページがメインスレッドをブロックするアセットの読み込みを開始すると、レンダリング機能がブロックされるため、フレームがヘッドセットに送信されず、しばらくするとブラウザがVRから追い出されますまた、ユーザーはヘッドセットを取り出し、[VRを入力]をもう一度クリックして(ユーザーのジェスチャーが必要です)、エクスペリエンスに戻る必要があります。

現在、OBJファイルのノンブロッキングロードの2つの実装を見ることができます。

  • (1)webworkersを使用してobjファイルを解析し、データをメインスレッドWWOBJLoaderに返します。
    ここでは、解析が同時に行われ、同時に複数のワーカーがいる可能性があります。 主な欠点は、データをロードしたら、ペイロードをメインスレッドに返送して3つのオブジェクトインスタンスを再構築する必要があり、その部分がメインスレッドをブロックする可能性があることです。
    https://github.com/kaisalmen/WWOBJLoader/blob/master/src/loaders/WWOBJLoader2.js#L312 -L423
  • (2)setTimeOutを使用した遅延解析によるメインスレッドの約束: Oculus ReactVR :このローダーは、setTimeoutを呼び出してメインスレッドをブロックしないように、小さなタイムスロットを使用して行を読み取り続けます//github.com/facebook/react-vr/blob/master /ReactVR/js/Loaders/WavefrontOBJ/OBJParser.js#L281 -L298
    このアプローチでは、各タイムスロットでいくつかの行を解析するだけなので、読み込みが遅くなりますが、解析が完了すると、追加のオーバーヘッドなしで3つのオブジェクトを使用できるようになるという利点があります。

どちらにも長所と短所があり、私は正直なところ、その実装を評価するWebワーカーの専門家ではありませんが、ローダーを非ブロッキングバージョンに移植するために使用できる汎用モジュールに理想的につながる興味深い議論です。

助言がありますか?

/ cc @ mikearmstrong001 @kaisalmen @delapuente @spite

Suggestion

最も参考になるコメント

THREE.UpdatableTextureの最初の提案リリース

理想的には、THREE.Textureの一部である必要がありますが、最初にこのアプローチを検討します。

全てのコメント53件

Promise + worker +インクリメンタルベースのローダー(両方のポイントの組み合わせのようなもの)を持つことができます

ソースURLをワーカースクリプトに渡し、リソースをフェッチし、必要なバッファー、構造体、さらにはImageBitmapsを含む転送可能なオブジェクトの構造体を返します。 これは、three.jsの処理オーバーヘッドをあまり必要としないほど簡単なはずです。

GPUへのデータのアップロードは関係なくブロックされますが、display.rAFを介して、コマンドを異なるフレームに分散するキューを作成できます。 コマンドは、フレームごとに一度に1つずつ実行することも、操作の平均時間を計算して、現在のフレームバッジで実行するのに「安全」な数だけ実行することもできます(requestIdleCallbackに似たものがいいでしょうが、広くサポートされていません、およびWebVRセッションでは問題があります)。 また、bufferSubData、texSubImage2Dなどを使用して改善することもできます。

ワーカーと転送可能なオブジェクトのサポートは、特にWebVR対応のブラウザーでは、現時点ではかなり堅実です。

みなさん、こんにちは。このコンテキストであなたが興味を持つかもしれないプロトタイプを利用できます。 次のブランチを参照してください。
https://github.com/kaisalmen/WWOBJLoader/tree/Commons
ここでは、のメッシュプロビジョニング部分がWWOBJLoader2から完全に分離されています。
https://github.com/kaisalmen/WWOBJLoader/blob/Commons/src/loaders/WWLoaderCommons.js

WWLoaderCommonsすると、他のメッシュプロバイダー(ファイル形式ローダー)を簡単に実装できます。 基本的に、Webワーカーの実装がメッシュデータをメインスレッドに提供し、それを処理/シーンに統合する方法を定義します。 技術デモンストレーターとして機能するランダムトライアングルジャンクプロバイダー😉を参照してください:
https://github.com/kaisalmen/WWOBJLoader/tree/Commons/test/meshspray
https://kaisalmen.de/proto/test/meshspray/main.src.html

現在の実装でも、 WWOBJLoader2は転送可能なオブジェクト(ArrayBuffers / ByteBuffers)に依存して、ワーカーからメインスレッドへのMeshの生のBufferedGeometryデータを提供します。 時間的には、提供されたByteBuffersからのMeshの作成はごくわずかです。 より大きなメッシュがシーンに統合されると、レンダリングは停止します(データのコピー、シーングラフの調整...!?)。 これは常にソースに関係なく当てはまります(間違っている場合は訂正してください)。
WWOBJLoader2の「ストリーム」モードはこれらのストールをスムーズにしますが、OBJモデルの単一のメッシュピースの重量が50万の頂点である場合、レンダリングはより長い時間一時停止します。

オーダーメイドのブランチで何をしたのか、そしてその理由を詳しく説明するために、新しい号を開きました。
https://github.com/kaisalmen/WWOBJLoader/issues/11
この問題はまだスタブであり、詳細はまもなく続きます。

いくつかの数値を提供するために、 https: //threejs.org/examples/webgl_loader_gltf2.htmlのパフォーマンスプロファイルを示します。これは、2048x2048テクスチャを含む13MBモデルをロードします。

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

この場合、メインスレッドをブロックする主なものはテクスチャをGPUにアップロードすることであり、私が知る限り、WWからは実行できません。ローダーはテクスチャを徐々に追加するか、three.jsが内部で処理する必要があります。

不思議なことに、メインスレッドをブロックする最後のチャンクは、環境キューブマップの追加です。

react-vrの主な目的は、必ずしも実時間の観点から最適なローダーを用意することではなく、新しいコンテンツの読み込みが発生したときに突然の予期しないフレームアウトを引き起こさないことです。 これを最小限に抑えるためにできることはすべて、特にVRにとって有益です。

テクスチャは間違いなく問題であり、明らかな最初のステップは、オプションでそれらをインクリメンタルにロードすることです-大きなテクスチャの場合は一度に1行のセットです。 アップロードはクライアントプログラムに対して非表示になっているため、管理が困難になりますが、これがwebglレンダラーにもっとオープンに公開され、three.jsからのプレッシャーを取り除くことができます。

gltf解析の場合、テストで500ミリ秒のブロックがよく見られますが、これは重要であり、すべてのローダーに対してインクリメンタルアプローチを使用することをお勧めします(これもクローン可能である必要があります)

React VRの前提は、より多くの開発者を奨励するためにWebスタイルによって駆動される簡単な動的コンテンツを奨励することであり、これにより動的処理の改善がさらに強調されます。 ほとんどの場合、ユーザーが作成したアプリケーションの開始時にどのアセットが必要になるかはわかりません。

@kaisalmenリンクをありがとう

Elation Engine / JanusWebでは、実際にはすべてのモデル解析をワーカースレッドのプールを使用して実行します。これは非常にうまく機能します。 労働者が各モデルの完成読み込みをしたら、我々は使用してそれをシリアライズobject.toJSON() 、とメインスレッドに送信postMessage() 、その後、使用してそれを読み込むObjectLoader.parse() 。 これにより、ローダーコードのブロック部分のほとんどが削除されます。 ObjectLoader.parse()にはまだある程度の時間が費やされており、おそらく最適化できますが、全体的な対話性と読み込み速度は大幅に向上しています。 ワーカーのプールを使用しているため、複数のモデルを並行して解析することもできます。これは、複雑なシーンで大きなメリットになります。

テクスチャの面では、ええ、three.jsのテクスチャアップロード機能にいくつかの変更が必要だと思います。 texSubImage2Dを使用するチャンクアップローダーが理想的であり、上記のように、複数のフレームにわたって大きなテクスチャの部分的な更新を行うことができます。

Three.jsをベースとして使用する多くのプロジェクトにメリットがあるため、この変更に協力できれば幸いです。

texSubImage2Dを使うのは良い考えだと思います。
しかし、WebGLがテクスチャを非同期にアップロードしない理由もあります。
OpenGLと他のライブラリには同じ制限がありますか?

そして、私が考えているもう1つのことは、GLSLのコンパイルです。
フレームを落としますか? または十分に速く、私たちは気にする必要はありませんか?

はい、これはネイティブOpenGLでも問題です。シェーダーのコンパイルと画像データのアップロードは同期/ブロック操作です。 これが、ほとんどのゲームエンジンが、レベルを開始する前にすべてのコンテンツをプリロードすることを推奨または強制する理由です。一般に、ハードドライブからでも新しいリソースをロードするにはパフォーマンスへの影響が大きすぎると考えられており、ここでは非同期で実行しようとしています。インターネットを介して...実際には、ほとんどのゲーム開発者よりも難しい問題があります。新しいコンテンツをその場でストリーミングできるようにするには、より高度な手法を使用する必要があります。

将来、新しいImageBitmap APIを使用する場合、テクスチャのアップロードの問題は少なくなります。 https://youtu.be/wkDd-x0EkFU?t=82を参照して

ところで: @spiteのおかげで、プロジェクトにはすでに実験的なImageBitmapLoaderがあります。

@ Mugen87実際、私はすでにElation Engine / JanusWebのImageBitmapですべてのテクスチャロードを行っています-これは間違いなく役立ち、Three.jsコアに統合する価値がありますが、WebGLでのテクスチャの使用には2つの主な費用がかかります-画像のデコード時間、および画像のアップロード時間-ImageBitmapは最初のものにのみ役立ちます。

これにより、私のテストではCPUをブロックする時間が約50%短縮されますが、GPUに大きなテクスチャ(特に2048x2048以上)をアップロードすると、簡単に1秒以上かかる場合があります。

@jbaicoianuが提案していることを試してみると便利です。 とにかく、メインスレッドの代替を選択した場合、これはsetTimeoutではなくrequestIdleCallbackに完全に一致するようです。

皆さんの意見に同意します。ワーカーにすべてをロードして解析し、必要なオブジェクトをメインスレッドに作成して戻し(非常にコストがかかる場合は、いくつかの手順で実行できます)、レンダラーに増分ロードを含めるというアプローチを信じています。
MVPの場合、maxTexturesUploadPerFrame(デフォルトでは無限)を定義でき、レンダリングはその数に従ってプールからのロードを処理します。
次の反復では、 @ spiteがコメントしたように、平均を測定し、ブロックする前の安全な範囲時間に基づいてそれらを自動的にアップロードするロジックを追加できます。 これは、最初は各テクスチャに対して1つの単位として実行できますが、その後、より大きなテクスチャのチャンクを段階的にアップロードするように改善できます。

requestIdleCallbackは素晴らしいでしょうが、広くサポートされておらず、WebVRセッションで問題があります

@にもかかわらず私はあなたの文章に興味があります、あなたは問題があるとはどういう意味ですか?

texSubImage2Dを使用してテクスチャを段階的に更新するTHREE.UpdatableTextureがありますが、three.jsを少し調整する必要があります。 アイデアは、サポートを追加するためのPRを準備することです。

requestIdleCallback(rIC)について:

  • まず、ChromeとFirefoxでサポートされており、簡単にポリフィルできますが、ポリフィルバージョンでは目的がわずかに損なわれる可能性があります。

  • 2番目:提示するときにwindow.rAFの代わりにvrDisplay.requestAnimationFrame(rAF)を呼び出す必要があるのと同じ方法で、このcrbugで説明されているように、同じことがrICにも

別の考慮事項:texSubImage2D(256x256または512x512)を使用して1つの大きなテクスチャを複数のステップでアップロードできるようにするには、オフセットおよびクリッピング機能を備えたWebGL2コンテキストが必要です。 それ以外の場合は、アップロードする前に、基本的にクライアント側でタイル化されたキャンバスを介して画像を事前にクリップする必要があります。

@spite良い点ですが、プレゼンテーション時にrICが呼び出されないことは考えていませんでした。最初は、display.rICが必要だと思いましたが、.rICはウィンドウにアタッチし、ウィンドウまたはディスプレイが呼び出されるときに呼び出される必要があると思います。両方ともアイドル状態です。
私はwebvrの仕様の議論でこれに関連することは何も聞いていないと思います@kearwoodはおそらくより多くの情報を持っていますが、間違いなく私たちが取り組むべき問題です。

あなたのUpdatableTexturePRを見るのを楽しみにしています! :)それが単なるWIPであっても、議論の一部をそこに移すことができます。

多分ローダーはこのようなものになるかもしれません...

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 );
        }
    }
} )();

THREE.UpdatableTextureの最初の提案リリース

理想的には、THREE.Textureの一部である必要がありますが、最初にこのアプローチを検討します。

@mrdoobまったく同じコードをワーカーにパイプすることのメリットがわかりますが、それはすっごく間違っていると感じます😄。 スクリプトのシリアル化、ブロブ、再評価の影響はどうなるのだろうか。 ひどいことは何もありませんが、ブラウザがこの癖に最適化されているとは思いません🙃

また、理想的には、リソース自体のフェッチはワーカーで行われます。 そして、ブラウザのparser()メソッドには、three.js自体のimportScriptsが必要だと思います。

しかし、同期/非同期ローダーを定義するための単一のポイントは、キックアスです!

@mrdoob builder関数は完全に汎用的で、すべてのローダーに共通である可能性があります(WIP:https://github.com/kaisalmen/WWOBJLoader/blob/Commons/src/loaders/support/WWMeshProvider.js#LL215- LL367;更新:関数でまだ分離されていません)。 入力データがTHREEオブジェクトを参照せずに純粋なjsオブジェクトに制限されている場合(それはあなたが考えていることですよね?)、ワーカーにインポートする必要なしにシリアル化可能なワーカーコードを構築できます( WWOBJLoaderはします)。 これはジオメトリにとっては簡単ですが、マテリアル/シェーダー(ファイルで定義されている場合)はビルダーでのみ作成でき、以前はparserによってJSONとしてのみ記述されていました。
ワーカーは、すべての新しいメッシュとその完了を通知する必要があると思います。 これは次のように変更できます。

// 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;
},

ワーカービルダーutilは便利です+パーサーをそのまま使用するというあなたの考えと矛盾しないいくつかの一般的な通信プロトコルですが、それはいくつかのラッピングが必要だと思います。 WWOBJLoaderの進化に関する現在の状態: https

Update2:

  • パーサーはステートレスであるべきだと思いますか? builderでも問題ありませんが、 parserの動作を調整するためにいくつかのパラメーターを設定できることは理にかなっています。 これは、構成パラメーターが解析とは無関係にワーカーに転送可能であることも意味します
  • 汎用構成オブジェクトを食べるrun関数のようなものがあると便利です。 ジェネリックディレクターは、任意のローダーに指示を与えることができます(これは現在、 WWOBJLoaderの特注のCommonsブランチで機能しています)
  • 進行中の開発: WWOBJLoader2OBJLoaderを拡張し、解析をオーバーライドするようになりました。 つまり、両方の解析キャップがありますが、クラスは異なります。 提案に近づいていますが、まだ一致していません。 一部のパーサーコードを統合する必要があり、最終的には両方のクラスを融合する必要があります

今のところ以上です。 フィードバックを歓迎します😄

@mrdoobローダーのコードからワーカーをその場で構成するというアイデアが好きです。 私の現在のアプローチは、結合されたアプリケーションjs全体をロードし、メインスレッドとは異なるエントリポイントを使用するだけであり、必要なコードだけでワーカーを構成するほど効率的ではありません。

メインスレッドに戻すときにこれらのTypedArrayを転送可能としてマークするのは簡単なので、ワーカー間を渡すためにトリミングされた送信形式を使用するアプローチが好きです。 現在のアプローチでは、ワーカーで.toJSON()メソッドを使用していますが、頂点やUVなどのJS配列を調べて、適切なTypedArrayタイプに置き換え、呼び出し時に転送可能としてマークします。 postMessage。 これにより、メインスレッドでの解析/メモリ使用量が少し軽くなりますが、ワーカーでの処理/メモリ使用量が少し多くなります-行うのは良いトレードオフですが、次のいずれかを導入することでより効率的にすることができます提案した新しい送信形式、または.toJSON()を変更して、オプションでJS配列の代わりにTypedArrayを提供します。

この単純化されたアプローチの2つの欠点は次のとおりです。

  • 既存のモデルローダーを書き直す必要があります。 多くのローダーは、組み込みの3つの名前空間クラスと関数を使用してタスクを実行するため、おそらく最小限のthree.jsコードのセットを取り込む必要があります。ワーカーを使用すると、これは注意が必要になります。
  • 送信形式は、オブジェクト階層と、さまざまなオブジェクトタイプ(メッシュ、SkinnedMesh、ライト、カメラ、Object3D、ラインなど)を適切にキャプチャする必要があります。

@spite 「また、理想的には、リソース自体のフェッチはワーカーで発生します。」 -これは、Elation Engine用のワーカーベースのアセットローダーを最初に実装したときの私の考えでした-私は4つまたは8つのワーカーのプールを持っていて、それらが利用可能になったときにそれらにジョブを渡し、次にワーカーがファイルをフェッチして解析しましたそれらをメインスレッドに戻します。 ただし、実際には、これが意味するのは、ダウンロードが解析をブロックし、一度にすべてを要求した場合、パイプライン処理などから得られるメリットが失われることです。

これに気付いたら、すべてのアセットのダウンロードを管理するための別のレイヤーを追加し、アセットダウンローダーがイベントを発生させて、アセットが利用可能になったときに通知します。 次に、バイナリファイルデータの転送可能ファイルを使用してこれらをワーカープールに渡し、効率的にワーカーに取り込みます。 この変更により、ダウンロードはメインスレッド上にある場合でもすべて高速に実行され、パーサーはデータを待つために親指をいじるのではなく、処理時にフルボアで実行できるようになります。 全体として、これはアセットの読み込み速度に関して私たちが行った最良の最適化の1つであることが判明しました。

テクスチャの読み込みのトピックについて、コンパニオンFramebufferTextureLoaderが付属する新しいFramebufferTextureクラスの概念実証を作成しました。 このテクスチャタイプはWebGLRenderTarget拡張し、そのローダーは、指定されたサイズのチャンクタイルにテクスチャをロードし、 requestIdleCallback()を使用してそれらをフレームバッファーに構成するように構成できます。

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

この例では、画像サイズとタイルサイズを選択するだけで、読み込みプロセスが開始されます。 まず、テクスチャを純粋な赤に初期化します。 画像のダウンロードを開始し(約10 MBなので、少し待ってください)、完了したら背景を青に変更します。 この時点で、 createImageBitmap()を使用して画像の解析を開始し、ファイルを解析します。それが完了すると、画像を効率的にタイルに分割するcreateImageBitmap()への呼び出しを含むアイドルコールバックをいくつか設定します。 。 これらのタイルは、複数のフレームにわたってフレームバッファーにレンダリングされ、一度にすべてを実行するよりもフレーム時間への影響が大幅に少なくなります。

注-FireFoxは現在createImageBitmapすべてのバージョンを実装しているようには見えず、タイルに分割しようとすると現在エラーをスローしています。 その結果、このデモは現在Chromeでのみ機能します。 FirefoxのcreateImageBitmapサポートロードマップのリファレンスを持っている人はいますか?

いくつかのクリーンアップを行う必要があります。このプロトタイプは少し面倒ですが、結果には非常に満足しています。クロスブラウザーの問題(キャンバスのフォールバックなど)を回避する方法を見つけたら、私はJanusWebのすべてのテクスチャのデフォルトとしてこれを使用することを検討してください。 フェードイン効果もちょっとすっきりしていて、最初にダウンサイズのバージョンを空想してブリットしてから、より詳細なタイルを徐々にロードすることもできます。

標準のテクスチャ参照ではなく、シーン内のすべてのテクスチャにフレームバッファを設定することが悪い考えである理由を誰かが考えることができるパフォーマンスまたは機能関連の理由はありますか? 最大については何も見つかりませんでした。 シーンごとのフレームバッファ、フレームバッファが設定された後、レンダリングしない場合は他のテクスチャ参照と同じですが、明らかな何かが欠けているような気がします。なぜこれが本当に悪い考えになるのか:)

@jbaicoianu re:firefoxのcreateImageBitmap。理由は、辞書パラメーターをサポートしていないため、画像の向きや色空間の変換をサポートしていないためです。 APIのほとんどのアプリケーションはかなり役に立たなくなります。 この問題に関連する2つのバグを報告しました: httpshttps://bugzilla.mozilla.org/show_bug.cgi?id=1335594

@それも私が思っていたのですが、オプション辞書をサポートしていないというこのバグを見たことがありますが、この場合はそれも使用しておらず、x、y、w、hオプションを使用しようとしています。 私が得ている特定のエラーは次のとおりです。

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

ImageBitmapFormatを引数として取る仕様にはcreateImageBitmapバージョンがないため、これは紛らわしいです。

標準のテクスチャ参照ではなく、シーン内のすべてのテクスチャにフレームバッファを設定することが悪い考えである理由を誰かが考えることができるパフォーマンスまたは機能関連の理由はありますか? 最大については何も見つかりませんでした。 シーンごとのフレームバッファ、フレームバッファが設定された後、レンダリングしない場合は他のテクスチャ参照と同じですが、明らかな何かが欠けているような気がします。なぜこれが本当に悪い考えになるのか:)

@jbaicoianu THREE.WebGLRenderTargetは、フレームバッファー、テクスチャ、およびレンダリングバッファーを保持します。 テクスチャをアセンブルしたら、フレームバッファとレンダーバッファを削除して、テクスチャのみを保持できます。 このような何かがこれを行う必要があります(テストされていません):

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

@wrr知っておくといいですね、ありがとう。 私は間違いなくこれについてもメモリ効率を渡す必要があります-パラメータを十分に変更すると、ある時点で必然的にクラッシュするので、まだ行っていないクリーンアップがあることを知っています。 このような他のヒントをいただければ幸いです。

@mrdoob@jbaicoianu私もそのアイデアが好きだと
OBJLoaderWWOBJLoaderのコード(initの作り直し、ワーカー命令オブジェクト、ゴミのマルチコールバック処理の置き換え、共通リソースの説明など)とすべての例(コード)を整理しました。 これで、両方のローダーを組み合わせる準備ができました。 彼らは私の暇な時間に応じて、来週のいつかあなたの青写真に従うことを願っています:
指示されたWWOBJLoader2テスト:
https://kaisalmen.de/proto/test/wwparallels/main.src.html
一般的なWorkerSupport指示されたユーザー:
https://kaisalmen.de/proto/test/meshspray/main.src.html
大きなzip形式のOBJファイルテスト:
https://kaisalmen.de/proto/test/wwobjloader2stage/main.src.html

上記の例が利用可能になったときに新しいコードで更新し、お知らせします。
更新2017-07-30: OBJLoader2WWOBJLoader2同一のパーサーを使用するようになりました。 それらは、データを一般的なビルダー関数に直接またはワーカーから渡します。
更新2017-07-31: WWOBJLoader2はなくなりました。 OBJLoader2は、 parseparseAsyncloadrunLoaderDirectorまたは手動でフィード)

更新2017-08-09:
更新を新しい投稿に移動しました。

OBJLoader2は、署名と動作がOBJLoaderと再び互換性があり(進化中にこれを壊しました)、 OBJLoader2parseAsyncloaduseAsyncフラグ。 私はそれがV2.0.0-ベータと呼ばれる準備ができていると思います。 ここで、現在の開発ステータスを確認できます。
https://github.com/kaisalmen/WWOBJLoader/tree/V2.0.0-Beta/src/loaders

ユーティリティおよび必要なサポートツールとして機能するLoaderSupportクラス(OBJとは独立)を抽出しました。 それらは、潜在的な他のワーカーベースのローダーに再利用できます。 以下のすべてのコードでは、名前空間THREE.LoaderSupport下に配置して、 OBJLoader2からの依存関係を強調しています。

  • Builder :一般的なメッシュ構築用
  • WorkerDirector :リフレクションを介してローダーを作成し、構成された数のワーカーでキュー内のPrepDataします。 ローダーを完全に自動化するために使用されます(MeshSprayおよびParallelsデモ)
  • WorkerSupport :既存のコードからワーカーを作成し、単純な通信プロトコルを確立するためのユーティリティクラス
  • PrepData + ResourceDescriptor :自動化に使用される説明、または単に例間の統一された説明に使用される説明
  • Commons :ローダーの可能な基本クラス(共通パラメーターをバンドル)
  • Callbacks :( onProgress、onMeshAlter、onLoad)は自動化と方向付けに使用され、 LoadedMeshUserOverrideonMeshAlterからの情報を提供するために使用されます(以下のobjloader2テストでの通常の追加)
  • Validator :null /未定義の変数チェック

@mrdoob @jbaicoianu OBJLoader2は、提案されているようにパーサーをラップするようになりました(グローバルに設定されるか、実行のためにPrepDataによって受信されるパラメーターで構成されます)。 Builderはすべての生メッシュを受け取り、パーサーはベースノードを返しますが、それ以外はブループリントと一致します。
OBJLoader2には、パーサーのシリアル化用のヘルパーコードがまだいくつかありますが、これらはおそらく必要ありません。
buildMeshes関数のコントラクト/パラメーターオブジェクトは依然としてOBJの読み込みの影響を強く受けているため、ビルダーはクリーンアップが必要です。したがって、まだ作成中であると見なされます。

コードには多少の磨きが必要ですが、フィードバック、ディスカッション、批評などの準備ができています...😄

例とテスト

実行とロードを使用するOBJローダー:
https://kaisalmen.de/proto/test/objloader2/main.src.html
run asyncとparseAsyncを使用するOBJローダー:
https://kaisalmen.de/proto/test/wwobjloader2/main.src.html
run async OBJLoader2の直接使用:
https://kaisalmen.de/proto/test/wwparallels/main.src.html
一般的なWorkerSupportの直接使用:
https://kaisalmen.de/proto/test/meshspray/main.src.html
大きなzip形式のOBJファイルテスト:
https://kaisalmen.de/proto/test/wwobjloader2stage/main.src.html

格好良い! OBJLoaderのこれらの変更に気づいていますか? #11871 565c6fd0f3d9b146b9434e5fccfa2345a90a3842

はい、これを移植する必要があります。 私はいくつかの再現可能なパフォーマンス測定を提案しました。 今週末の両方で作業を開始します。 いつr87をリリースする予定ですか? N-gonサポートは、日付によってはそれを実現する可能性があります。

@mrdoobhttps

ステータスの更新(コード):
作成されたワーカーは、メッセージによって受信されたパラメーターを介して、ワーカー内の任意のパーサーを構成できるようになりました。 WorkerSupportは、必要に応じて、または必要になった場合に独自のコードに完全に置き換えることができるリファレンスワーカーランナー実装(コード)を提供します。
ワーカーは、 WorkerRunnerRefImplrunメソッドでパーサーを作成して実行します( Parserはワーカースコープ内で使用できます。 this.applyPropertiesはのセッターまたはプロパティを呼び出します。パーサー):

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 );

    }
};

OBJLoader2.parseAsyncからのメッセージは次のようになります。

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

メッセージオブジェクトはローダーに依存しますが、ワーカー内のパーサーの構成は一般的です。
以前の投稿でリンクされた例で使用されているコードは、最新のコードで更新されています。

OBJLoader2の進化とサポート関数の抽出は、フィードバックが必要になる段階に達していると思います。 すべての例がリポジトリから上記のブランチに移植されたら、完全な要約を含むPRを開き、フィードバックをリクエストします

参考までに、上記のようにImageBitmapLoaderにワーカーを使用させるための進行中の作業を次に示します。 おそらくもっと興味深いことに、結果のいくつかの難しい数字: https

FirefoxのcreateImageBitmapは、辞書パラメーターをサポートしていないため、画像の向きや色空間の変換をサポートしていません。 APIのほとんどのアプリケーションはかなり役に立たなくなります。

これは残念です。 ☹️

@mrdoob ImageBitmapはテクスチャへのアップロードのブロックを少なくする必要があるため、 TextureLoader ImageLoaderImageBitmapLoaderに切り替える計画はありますか? createImageBitmap()は、最初の引数のみを渡すと、これまでのところFireFoxで機能しているようです。 (おそらく、 TextureLoader介して2番目以降の引数を渡す必要はありませんか?)

return createImageBitmap( blob );

createImageBitmap ()がオプション辞書をサポートすることImageBitmap WebGLRenderingContext.pixelStoreiを使用できないことです。 スペックから:

_TexImageSourceがImageBitmapの場合、これら3つのパラメーター(UNPACK_FLIP_Y_WEBGL、UNPACK_PREMULTIPLY_ALPHA_WEBGL、UNPACK_COLORSPACE_CONVERSION_WEBGL)は無視されます。 代わりに、同等のImageBitmapOptionsを使用して、目的の形式でImageBitmapを作成する必要があります。_

したがって、FFがオプション辞書をサポートしている場合にのみImageBitmapLoader切り替えることができると思います。 さらに、 Texture.premultiplyAlphaTexture.flipYようなプロパティは、現在ImageBitmapでは機能しません。 つまり、ユーザーが設定した場合、 ImageBitmap基づくテクスチャには影響しませんが、これはやや残念です。

ああ、わかりました。 私はその仕様を逃しました。

オプション辞書の重要性についてもここで説明します。

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

bugzillaのバグ(https://bugzilla.mozilla.org/show_bug.cgi?id=1367251、https://bugzilla.mozilla.org/show_bug.cgi?id=1335594)は、手つかずのままです... 2今何年? 私は彼らがそれを修正するのにこれほど長い時間がかかるとは思いませんでした。

したがって、問題は「技術的に」この機能がFFでサポートされていることですが、実際には役に立たないということです。 それを使用するために、それを使用するChromeのパスと、使用しない他のブラウザーのパスを設定できます。 問題は、Firefoxにはこの機能があるので、UAスニッフィングを行う必要があるということです。

実用的な解決策は、特徴検出を実行することです。フリップフラグ付きのcIBを使用して2x2イメージを作成し、読み返して値が正しいことを確認します。

FireFoxのバグについては、社内でも連絡します。 彼らの計画を聞いた後、回避策が必要かどうかを見てみましょう。

bugzillaのバグ(https://bugzilla.mozilla.org/show_bug.cgi?id=1367251、https://bugzilla.mozilla.org/show_bug.cgi?id=1335594)は、手つかずのままです... 2今何年? 私は彼らがそれを修正するのにこれほど長い時間がかかるとは思いませんでした。

うん、すみません、私は本当にしばらくフォローアップしませんでした-_-

したがって、問題は「技術的に」この機能がFFでサポートされていることですが、実際には役に立たないということです。 それを使用するために、それを使用するChromeのパスと、使用しない他のブラウザーのパスを設定できます。 問題は、Firefoxにはこの機能があるので、UAスニッフィングを行う必要があるということです。

実用的な解決策は、特徴検出を実行することです。フリップフラグ付きのcIBを使用して2x2イメージを作成し、読み返して値が正しいことを確認します。

うん、私は両方の解決策が本当にひどいことに同意します、そして私たちはそれらを避けるように努めるべきですので、これらのいずれかを掘り下げる前に私たちが私たちの側でそれをブロック解除できるかどうか見てみましょう

ImageBitmapアップロードパフォーマンステストを行いました。 5秒ごとにテクスチャをアップロードします。

通常の画像とImageBitmapを比較できます。

https://rawgit.com/takahirox/three.js/ImageBitmapTest/examples/webgl_texture_upload.html (通常の画像)
https://rawgit.com/takahirox/three.js/ImageBitmapTest/examples/webgl_texture_upload.html?imagebitmap(ImageBitmap

私の窓に見える

| ブラウザ| 8192x4096 JPG 4.4MB | 2048x2048 PNG 4.5MB |
| ---- | ---- | ---- |
| Chromeイメージ| 500ms | 140ms |
| Chrome ImageBitmap | 165ms | 35ミリ秒|
| Firefoxの画像| 500ms | 40ms |
| FireFox ImageBitmap | 500ms | 60ms |

texture.generateMipmapstrue

私の考え

  1. ChromeのImageBitmapで3倍優れたパフォーマンス。 非常に素晴らしい改善。
  2. FireFoxにはImageBitmapのパフォーマンスの問題がありますか? あなたもあなたのコンピュータで試すことができますか? モバイルでお試しいただくことも大歓迎です。
  3. ImageBitmapを使用しても、テクスチャのアップロードは大きなテクスチャに対してブロックされているようです。 たぶん、段階的に部分的にアップロードするテクニックか、ノンブロッキングのための何かが必要です。

ImageBitmapを使用しても、テクスチャのアップロードは大きなテクスチャに対してブロックされているようです。 たぶん、部分的なアップロード技術か、ノンブロッキングのための何かが必要です。

この問題の1つの解決策は、テクスチャ圧縮形式の使用とJPGまたはPNGの回避(したがってImageBitmap )である可能性があります。 このコンテキストでいくつかのパフォーマンスデータを見るのは興味深いでしょう。

はい、同意しました。 しかし、特にモバイルのような低電力デバイスでは、大きなテクスチャのブロッキングがまだ見られると思います。 とにかく、最初にパフォーマンスを評価します。

または、scheduled / requestIdleCallbacktexSubImage2Dを使用します

rIC = requestIdleCallback?

はい、忍者編集をしました

わかった。 はい同意しました。

ところで、私はまだ圧縮されたテクスチャに精通していません。 私の理解を確認させてください。 compressedTexImage2DImageBitmap受け入れないため、 ImageBitmap圧縮テクスチャを使用することはできません。

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

以前のTiledTextureLoaderの実験に戻りましたが、ビデオドライバーがクラッシュして再起動しているようです:(

(編集:実際には、最大のテクスチャ(16k x 16k-https://baicoianu.com/~bai/three.js/examples/textures/dotamap1_25.jpg)を直接Chromeにロードしているように見えますが、クラッシュの原因となっています。これは以前は問題なく機能していたため、Chromeの画像処理に多少の回帰があるようです)

requestIdleCallback、ImageBitmap、およびES6ジェネレーターを使用していくつかの実験を行い、GPUにアップロードするために大きなテクスチャを複数のチャンクに分割しました。 通常のテクスチャではなくフレームバッファを使用しました。texSubimage2Dを使用して画像データを入力している場合でも、メモリを事前に割り当てる必要があるため、GPUに大量の空のデータをアップロードする必要がありますが、フレームバッファは作成できます。単一のGL呼び出しで初期化されます。

これらの変更のリポジトリは、 https://github.com/jbaicoianu/THREE.TiledTexture/から引き続き入手できます

私が実験について覚えていることからのいくつかのメモ:

  • requestIdleCallbackは、テクスチャのロード中のジャンクを確実に減らすのに役立ちましたが、合計ロード時間が大幅に増加しました。
  • 少し余分な作業を行うことで、最初にテクスチャのダウンスケールバージョンをアップロードし、次にフル解像度のデータをよりゆったりとしたペースで入力することで、これを軽減できます。
  • ES6ジェネレーターは、コードを理解しやすくし、メモリを無駄にすることなく記述しやすくするのに役立ちましたが、おそらくこれには実際には必要ありません。

私の結果も同様でした。アップロード速度とぎこちなさの間にはトレードオフがありました。 (ところで、私はこのhttps://github.com/spite/THREE.UpdatableTextureを作成しました)。

2番目のオプションをWebGL1で機能させるには、実際には2つのテクスチャ、または少なくともUV座標のモディファイヤが必要になると思います。 WebGL 2では、ターゲットテクスチャとは異なるサイズのソースをコピーする方が簡単だと思います。

ええ、texSubImage2Dでは、このようなサイズ変更は不可能だと思いますが、フレームバッファーを使用する場合は、OrthographicCameraを使用してテクスチャフラグメントを含む平面をレンダリングしているので、平面のスケールを変更するだけです。そのドローコール。

FireFoxでのImageBItmapのパフォーマンスの問題について、bugzillaでバグを開きました

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

私は、テクスチャに関連付けられたデータが実際にGPUに読み込まれ、このスレッドに出くわしたときを理解しようと努めてきました。 私の特定のユースケースでは、ローカルjpeg / gifファイルのテクスチャへのロードとデコードについては気にせず、テクスチャデータをGPUにプリロードしようとすることだけを気にしています。 このスレッドを読んだ後、私はそれが両方の問題に対処しているのか、それとも前者だけに対処しているのか完全にはわからないことを告白しなければなりません。 後者だけを気にしているので、別の解決策を探す必要がありますか、それともテクスチャデータをGPUに強制的にロードするのに役立つ何かがここにありますか?

このページは役に立ちましたか?
0 / 5 - 0 評価