Breaking change to 1.12: rendering sub-images

1.12 に入る破壊的変更: サブ画像の描画

Hajime Hoshi
2020-06-28

This artcile includes the information in June 2020. There's a possibility the plan will change.

この記事は 2020 年 6 月現在の情報であり、予定を変更する可能性もあります。

TL;DR

要約

Ebiten is adding a breaking change to 1.12 (the current master branch). Ebiten will no longer check the source-image region when rendering an image, and unexpected pixels might be rendered in some cases. This rendering result's difference might be seen when all the following conditions are satisfied:

Ebiten は 1.12 (現在の master ブランチ) から破壊的を導入する予定です。 Ebiten は、画像の描画の際、描画元領域のチェックをしないようになり、そのためいくつかのケースでは予期しないピクセルが描画されることがあります。この描画結果の違いは、以下の条件のすべてが満たされた場合に発生しえます:

For example, rendering tiles on texture atlas with scaling or rotating might cause a different result from 1.11. The rendering result might include pixels in its adjacent area. On the other hand, for example, rendering 9-patches should not be problematic since the graphics are continuous.

例えば、テクスチャアトラス上のタイルを拡大縮小や回転をさせて描画する場合、 1.11 とは違った結果になりえます。描画結果が隣接する領域のピクセルを含むかもしれません。一方例えば、 9-patch で描画するような場合は、画像が連続しているので問題にはなりません。

The next image is an example of a sub-image and the graphics that are not continuous on the right side.

次の画像はサブ画像、および右側に連続しない画像を含む例です。

If necessary, you'd need to create a texture atlas with paddings for each area.

必要ならば、テクスチャアトラスの各領域にパディングを追加する必要があります。

Background

背景

Ebiten has a feature called automatic texture atlases. Even if you create many ebiten.Image objects, Ebiten tries to allocate them onto one texture atlas when possible. This is for an efficient rendering by reducing graphics commands sent to the GPU. Additionally, Ebiten users don't have to care about this. That's great!

Ebiten には自動テクスチャアトラスと呼ばれる機能があります。たとえ大量の ebiten.Image を作ったとしても、 Ebiten はそれらを一つのテクスチャアトラス領域上に可能な限り確保しようとします。これによって GPU に送られるグラフィックス命令を減らすことができ、効果的な描画が実現できます。さらに、 Ebiten ユーザーはこのことを気にする必要がないのです。素晴らしい!

When Ebiten rendered a part of a texture atlas, Ebiten did a hack not to expose the pixels in the adjacent areas. Without the hack, such adjacent pixels can be exposed, especially when the image is rendered with rotating or scaling. This is a general issue of graphics programming. As the hack, Ebiten did two things:

Ebiten がテクスチャアトラスの一部を描画するとき、 Ebiten は隣接する領域のピクセルを露出させないために、あるハック (小細工) をしていました。このハックがないと、特に画像を回転または拡大縮小させて描画したときに、隣接するピクセルが露出してしまいます。これは一般的なゲームプログラミングの問題です。ハックとして、 Ebiten は次の 2 つのことをしていました:

Both were problematic in terms of code maintainability, and the second hack was especially problematic. One reason is performance. This hack requires 'if' branches in the shader programs and degrades the performance. Another thing is that this hack made it hard to define custom shaders, we are now introducing at 1.12. In the custom shaders, we wanted the users to take pixels from an image in an easy way, but we would need a special function for this purpose to perform this hack.

両方ともコードのメンテナンス性の観点から問題でしたが、特に 2 つ目のハックが問題でした。一つの理由はパフォーマンスです。このハックのせいで if 分岐がシェーダーに追加されてしまい、パフォーマンスが落ちていました。もう一つの理由は、 1.12 に向けて導入中のカスタムシェーダーの定義が難しくなることです。カスタムシェーダー内で、画像からピクセルを取るときに簡単な方法で取れるのが望ましいのですが、このハックを実現するために特殊な関数などを用意しなければなりません。

Removing the hacks

ハックの削除

We decided to remove these hacks. Instead, all the images are allocated onto the automatic texture atlas with 1px paddings. Even without the check of the source region, the outside pixels are transparent color and then this never violates the adjacent areas.

我々はこれらのハックを削除することにしました。代わりに全ての自動テクスチャアトラス上の画像は 1px のパディング付きで確保されます。描画元領域のチェックがなくとも、外側のピクセルは透明であり、隣接する領域を侵害してしまうことは決してないのです。

However, Ebiten can treat sub-images, which are created by (*ebiten.Image).SubImage function. They are parts of existing images. They cannot be allocated with paddings independently. There was an idea to allocate all the sub-images as independent images with paddings, but this was not good for performance and memory. If a user called SubImage onto an image many times with 1px-shifted regions, each sub-image would be allocated.

しかし、 Ebiten はサブ画像も取り扱えます。サブ画像は (*ebiten.Image).SubImage 関数で作られた画像です。これらは既存の画像の一部です。サブ画像はパディング付きで独立して確保することができません。全てのサブ画像をパディング付きの独立した画像として確保する案もありましたが、これはパフォーマンスとメモリの観点から良くありませんでした。もしユーザーが画像上で SubImage を 1px ずつずらした領域で大量に呼んだ場合、それぞれのサブ画像分領域が確保されてしまいます。

Then, we gave up to support such paddings around sub-images. As a result, rendering a sub-image can cause different results compared to 1.11. The adjacent area's pixels can be rendered, especially when the image is rotated or scaled.

よって、サブ画像についてはパディングを追加するのは諦めました。結果として、サブ画像の描画は 1.11 と比べると違った結果を引き起こすことがあります。特に、画像を回転または拡大縮小させた場合、隣接する領域のピクセルが描画されることがあります。

Example

This is an example that causes bleeding edges by a scaled and rotated sub-image. You can see an unexpected green pixel on the right side. This rendering is tested on the latest master branch (aee5d6d7). The result depends on the machine's GPU.

拡大と回転したサブ画像によって、隣接領域が見えてしまった描画の例です。右側に本来描画されるべきではない緑色のピクセルが描画されています。この描画は最新の master ブランチ (aee5d6d7) でテストされました。結果は GPU によって異なります。


  

What you should do

あなたがすべきこと

If you have your own texture atlas, and the parts are rendered with scaling or rotating, and the parts' graphics are not continuous, you need to add paddings for each part.

もし自作のテクスチャアトラスがあり、かつそれらの一部を拡大縮小または回転させて描画させ、かつそれらの画像が連続していない場合、パディングをそれぞれのパートに追加する必要があります。