ひらしょー

平山尚が技術のことを書く場所。Unityが多そう。

テクスチャ圧縮の厄介さ

Unity、というか、スマホ向けにゲームを作るにあたって、

テクスチャ圧縮は悩ましい問題だ。

 

ゲーセン及び家庭用では「とりあえずBC3(DXT5)」

な時代が長かったし、BC4やBC5、A8やL8といった

フォーマットも選択肢としてあった。

対応機種が複数であっても、

手元に全ての機械があったし、仕様もはっきりしていた。

圧縮は自前でやっていたので、

デザイナーが自力でやる、サーバに置いて自動でやる、

みたいな選択肢も自分側にあった。

BC7が現れて圧縮に時間がかかるようになった時にも、

マシン並列やGPU化で高速化する選択肢があった。

 

だがUnityでスマホ向けとなると話が違う。

 

まず、機種が全部手元にない。比率も確実にはわからず、

本当に動くかもわからないとなると、

安全側に振らざるを得ない。

いろんな形式を用意して、機種によって最適なものを

使ってもらう、というのも手間をかけられるなら良かろうが、

手間イコール金と時間なのが難しい。

そういうわけで、「全機種で使えるであろう形式」

に絞れると楽、という事情がある。

あるいは古い機械を相手にしない、という選択もありうるが、

それは技術というよりは商売の問題だろう。

 

もう一つ面倒なのは圧縮がUnityの中で行われ、

その間は操作不能になることだ。

また、複数バージョン持っておくこともできず、

設定を変えれば古いものは消えてしまう。

とにかく圧縮のやり方に関して自由度がない。

別のマシンで並列化とか、自作の高速仮圧縮で

とりあえず体裁だけ整えるとか、

低画質版と高画質版を両方持っておくとか、

そういうことができない。

なおさら、全部の機械で動くと期待できる形式だけを

用意して終わりにしたくなる。

 

Unityで使える形式は、まず、無圧縮32bit。24bitとあっても、

メモリの中では32bit食う機械も多かろう。

そういう機械では24bitでロードすると32bitへの変換が

走って余計に遅くなる。

無圧縮と言えば32bit、と考えて良いのではないだろうか。

 

16bit系は565っぽいもの(RGB16)と、4444っぽいもの(RGBA16)。

5551、つまり色15bitにアルファ1bitのフォーマットは

使えないように見える。

 

8bitの1チャネル系はA8だけ。

シェーダでどう出てくるかは確認していないが、

RGBが1でアルファだけ値が出てくるとすると、

若干使いにくい。

 

もっと減る形式として古い機械でも動くものは、

Android用にETC1、iOS用にPVRTC。

ETC1は透明度を持てず、

ETC1、PVRTC共にかなり劣化の仕方にクセがあって、

苦手とする画像が異なる。

余計な手間を減らそうと思うと、

Androidではこの画像が汚ないのでどうにかしたい」

みたいなことは極力避けたい。

そもそも実機で確認する、ということ自体が手間なので、

UnityEditor上で劣化の状況まで確認できる方が良いのだ。

とはいえ1画素4bitという圧縮率は捨てるには惜しいので、

ほどよい匙加減が求められる。そのへんが面倒くさい。

 

両方で同じものが動くようになるのは、

ASTCの普及後ということだろう。

人によっては「すでに普及している」と考える人もいるだろうが、

それは商売の問題だ。

個人的には私が持っているスマホが対応していないので、

私は「ASTCはまだ普及してない」と思っている。

 

 

さて、そんな面倒くさいテクスチャ圧縮を

何のためにやるかと言えば、

 

- 実行時のメモリ使用量の削減、

- 実行速度の向上

 

この二つだ。ストレージ容量や、ネットでの通信容量

に関しては、pngやjpgにした方が余程減るわけで、

テクスチャ形式なんて気にする必要はない。

しかしpngやjpgは展開しない限り描画に使えないわけで、

ブラウザだってメモリの中ではjpgやpngは展開されている。

なのでメモリ消費量を減らそうと思えばjpgやpng

役に立たない。GPUがハードウェアとして

対応している形式でなければどうにもならない。

 

となると、jpgなりpngなりで転送、

ストレージに置いておいて、

メモリにロードしてからGPU向けの圧縮形式に変換して使う、

というのが理想なはずなのだが、そうは行かない事情がある。

このGPU向けの圧縮形式への圧縮に、

凄まじく時間がかかるからだ。

とても実行時にできるような代物ではない。

ただし、よほど品質を妥協すれば、

jpgで読んで圧縮し直して使う、ということは実際可能だ。

Unityでもやってできないことはないように見える。

ただ凄まじく面倒くさいことになるし、

品質も論外になるため、

かなり限られた用途でしかやらないだろう。

例えば、莫大にたくさんある画像から実行時に何かしら合成して、

しかもそれを多量にメモリに載せないといけない、とかだろうか。

例えばスポーツ選手が数千人入ったゲーム、

みたいなものだと、顔写真データは極力減らさないと

転送が遅くなりすぎるし、ストレージも辛い。

なのでjpgで持っておいて、ロードしてから展開したくなる。

しかし同時に多数の選手が出てくるなら、

無圧縮だとメモリの消費も痛いだろう。

後述するように性能の問題もある。

そこで品質に妥協してでも圧縮をかけ直す、という選択は

ありえる。実際にやる例があるかどうかは知らないが。

 

そして性能にも影響する。テクスチャをメモリから画像データを

演算器(シェーダ)に運んで、計算し、

それをまたメモリに書き出すのが描画というプロセスなわけだが、

最初にテクスチャをメモリから運ぶところは量が多いほど

時間がかかる。電気も食う。

さらに、演算器から見ればメモリはずいぶんと遠くにあるため、

演算器の近くに小さなメモリ(キャッシュ)を置いておいて、

良く使うものはそこに置いておくようにしているのだが、

データが大きいとすぐにそれが一杯になって、

遠くのメモリまで見に行かねばならなくなる(キャッシュミス)。

結果、テクスチャがデカいと性能も落ち、電気も食う。

 

しかし、これに関しては実際どれくらい遅くなるかは

機械による。そしてその機械が全て手元にあるわけではない。

手元の機械で測っても、違う挙動をする機械はありうる。

メモリが高性能でデカいテクスチャでもビクともしない

こともあるだろうが、安物はそうは行くまい。

実際、メモリと演算器をつなぐ道幅を広くすると

テキメンに機械の値段が上がるので、

安物は道幅を狭くしてあることが多い。

iPhoneは基本高級品なので、iPhoneでばかり見ていると

私のようにymobileで「安いのくれ」と言って買うような

タイプの人を見逃すことになる。

スマホが出始めの頃は毎年買い換えるようなタイプの人が

多数いて、それに応えるように

毎年性能が倍々になっていく世界だったが、

今はもはやそうでもない。

「頑丈で防水」とか「デザインがいい」とか、

そういう理由で買われる機械の性能を高める理由はあまりない。

実際、私が2017年後半に買ったS2という機械は、

2012年発売のiPhone5にも大きく劣る。

「新しい機械は速い」はもはや成り立たないのだ。

要求性能を低くできるなら、それに越したことはないだろう。

テクスチャ圧縮はそのための武器になる。

 

ただ、逆に言えば、メモリ消費量と性能の問題がないなら、

こんなに面倒なテクスチャ圧縮なんてしなくてもいい、

ということではある。テクスチャ圧縮なしで問題ない

サイズや性能要求に収めて開発する、という選択はアリで、

たぶんそれはコストを結構大きく下げる。

「メモリ4倍だぞ?圧縮しないとかありえんだろ」

と私なんかはすぐ思ってしまうが、それが持ちこむ

面倒の大きさは結構バカにならない。

ましてUnityの場合それを解決する手段がなく、

インポート時間やビルド時間の増加はボディブローのように

ゲーム開発を蝕むのだ。jenkinsに出したから大丈夫、

などという単純な話ではない。

圧縮済みのテクスチャをインポートできたらいいのにと

本気で思うが、今できないということは

そういう要望はあんまりないということなんだろうな。