Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
0
Help us understand the problem. What are the problem?

Flash Advent Calendar 12日目 - WebGL導入への道のり05 -

WebGLで色々な描画ができるようにはなってきたのですが
複雑な描画の実装を進めていくとGPUの負荷が高くなり、速度が低下してきました。

その時に起きた低下原因と解消方を書いていこうと思います。

  1. Framebufferを使っていない
  2. コール数が多い(createしたbuffer情報を使い回せていない etc...)
  3. 必要最低限の領域に対して描画を行う

Framebufferを使っていない

そもそもとして、メインのFramebufferだけで描画処理を行っていた...
オフスクリーンレンダリングができていない状態でした。

この時、WebGL1.0で実装したいたので、Framebufferを導入するとアンチエリアスが使えない!?
っという事態になりました。
WebGL2.0への移行が必須になりシェーダーをWebGL2.0に対応する課題が発生したのですが
それはまた別の機会に・・・orz

メインのFramebufferに描画するとcanvas側に描画が適用される為、無駄なI/Oが発生してしまいます。
サブのFramebufferは完全にメモリだけの描画になるため、負荷が軽減されます。

// 描画だけで使うFramebufferを生成
this.frameBuffer = this.gl.createFramebuffer();
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.frameBuffer);

// 書き込むtextureを生成
this.texture = this.gl.createTexture();
this.gl.activeTexture(this.gl.TEXTURE0);
this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);

this.gl.framebufferTexture2D(
    this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0,
    this.gl.TEXTURE_2D, this.texture, 0
);

// 描画情報を書き込むRenderbufferを生成
this.stencilBuffer = this.gl.createRenderbuffer();

this.gl.renderbufferStorage(
    this.gl.RENDERBUFFER,
    this.gl.STENCIL_INDEX8,
    width, height
);

this.gl.framebufferRenderbuffer(
    this.gl.FRAMEBUFFER, this.gl.STENCIL_ATTACHMENT,
    this.gl.RENDERBUFFER, this.stencilBuffer
);

この仕組みを導入する事でGPUの負荷は結構軽減しました。
ただ、この仕組みの導入で別の問題が発生しました。

コール数が多い

WebGLの速度関連で検索するとよく見かける言葉です・・・

createしたbuffer情報を使い回せていない

  • createTexture
  • createRenderbuffer
  • createBuffer

などなど
作ったbuffer情報が不要になったら破棄していたのですが
そもそもcreate系の関数のコール負荷が非常に高かったです。

なので、使い終わったbufferをpoolして使い回す事でGPUへの負荷を軽減しました。

また、次に利用するbufferのサイズが一致していれば
さらに余計な処理を省く事ができます。


/**
 * @param  {number} width
 * @param  {number} height
 * @return {WebGLRenderbuffer}
 * @public
 */ 
getStencilBuffer (width, height)
{
    for (let idx = 0; idx < this.objectPool.length; idx++) {
        const stencilBuffer = this.objectPool[idx];
        if (stencilBuffer.width === width && stencilBuffer.height === height) {
            this.objectPool.splice(idx, 1);
            return stencilBuffer;
        }
    }

    const stencilBuffer  = this.gl.createRenderbuffer();
    stencilBuffer.width  = 0;
    stencilBuffer.height = 0;
    return stencilBuffer;
}

/**
 * @param  {number} width
 * @param  {number} height
 * @return {WebGLRenderbuffer}
 * @public
 */ 
create (width, height)
{
    const stencilBuffer = this.getStencilBuffer(width, height);

    if (stencilBuffer.width !== width || stencilBuffer.height !== height) {
        stencilBuffer.width  = width;
        stencilBuffer.height = height;

        this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, stencilBuffer);
        this.gl.renderbufferStorage(
            this.gl.RENDERBUFFER,
            this.gl.STENCIL_INDEX8,
            width, height
        );
    }

    return stencilBuffer;
}

この例だと

  • bindRenderbuffer
  • renderbufferStorage

が省略されます。
コール数が減るとGPUの負荷がかなり軽減されます。

必要最低限の描画領域に対して描画を行えていない

scissorなどで描画に必要な領域だけに対して描画したりclearする事でGPUの負担を減らす事ができました。

/**
 * @param  {number} x
 * @param  {number} y
 * @param  {number} w
 * @param  {number} h
 * @retuen {void}
 * @public
 */
clear (x, y, w, h)
{
    this.gl.enable(this.gl.SCISSOR_TEST);
    this.gl.scissor(x, y, w, h);
    this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.STENCIL_BUFFER_BIT);
    this.gl.disable(this.gl.SCISSOR_TEST);
}

まだまだ、やれる事はいっぱいあると思うのですが
大きな改善が見れたポイントを書きました。

明日は、テキスト入力をどうやって再現したかを書こうと思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
0
Help us understand the problem. What are the problem?