X1turbo Agency

X1turbo Agency ブログ

8bitコンピュータ X1turboの探求サイト

X1turbo Agency

X1用ゲームライブラリ SGL

X1 SGLとは?

X1 SGLとは X1/turbo用のゲームにおける性能を引き出す事を探求したライブラリです。

きっかけ

X1はハードウェアスプライトの機構を持っておらず、描画時にチラついたり、描画負荷の重さからプレーン数を減らして色数が少なくなる事も多いです。
Newグラディウスデモを制作時に、これらを改善するべく自分なりにライブラリを作ったらどうなるか?というのを探求してみました。
このページでは主に今まで分散していたSGL記事をまとめたものです。
その中でも、ソフトウェアスプライトでの取り組みを説明します。

問題点

ソフトウェアスプライトを実現するために問題点をピックアップしてみます。

X1では重ね合わせの処理が重い

ソフトウェアスプライト処理は、X1ではGRAMのデータを ANDを取ってORを使ってはめ込む重ね合わせ処理を行います。

重ね合わせ処理

バンク切り替えは不要なものの、I/O空間にGRAMがあるため比較的遅いI/O命令でのアクセスが必要です。
しかもそれらをRGB 3プレーンに対して処理を行うため、非常に重い処理になります。
この処理を軽減するためには、できるだけ重ね合わせを行わないことがポイントになります。
必要な時だけ重ね合わせを行って、それ以外は単純に書込みだけを行えば処理を軽減できます。
そこで重ね合わせを制御する機構としてビットラインバッファという制御ワークを用意します。
下の図はそのイメージ図です。

ビットラインバッファ

このビットラインバッファのアドレスはライン単位になっており、bitが各ラインが書込み済みかどうかを示しています。
X1のGRAMは変則的な並びですが、描画時にも処理を8ラインをひとまとめにすることで効率化を図っています。
ビットラインバッファを画面消去時もすでに消去済みかどうかをチェックして、2重に消去しないことで処理を軽減します。

描画プレーン数を減らして処理速度を改善する

描画プレーン数を減らす事で高速化を行いますが、出来るだけ表現力を上げる工夫をしてみます。
以下の様にパレットを設定します。

パレットセット

1プレーン描画では以下の色を描画します。
負荷が最も軽いため、数が多く登場するキャラクタに使用します。

単体プレーンのパレット

2プレーンの組み合わせを、Blue + Red,Blue +Green,Red + Greenと分けることで、少ないプレーン数で色数を増やしています。

  • 青色系統のキャラクタ (白/水色/青)
  • 赤色系統のキャラクタ (白/黄色/赤)
  • その中間 (白/水色/赤)

2プレーンパレットの組み合わせ

ちらつき軽減のためにダブルバッファを使用する

描画中の様子が見えてしまうとチラツキが発生します。
チラツキへの対処はダブルバッファが有効です。
X1は320x200x8色の描画ページを2枚持つことができます。これを活用してダブルバッファを実現します。
ページは、0000-07ffhのアドレスを分割して、0000h-03ffh, 0400h-07ffhの2ページで切り替えます。
この2ページを表示ページと描画ページに分けます。
表示ページを表示している間に、裏で描画ページに対して書込みを行います。
これをVSyncのタイミングで、表示と描画を切り替えます。 ダブルバッファは結構強力な機能です。3D用アプリケーションにも活用できると思います。

実装の詳細について

ビットラインバッファ

ビットラインバッファは書込まれているデータがあるかどうかを、ラインに対応したビットをOn/Offすることで判定を行います。
ビットラインバッファの配置アドレスは、0f800h とORすることで算出します。
アドレスは固定になりますが、メインメモリの 0f800-0ffffh の2KBをビットラインバッファに割り当てます。
この方法のメリットは、GRAMのアドレスからビットラインバッファへのアドレスを求める事ができることです。
ビットラインへのアドレス計算を別に持つ必要が無いため高速に扱えます。
画面消去時も、GRAMアドレスからビットラインバッファへのアドレスを算出することができます。

ビットラインバッファでの描画とプレーン数最適化による描画コスト削減

ビットラインバッファを参照して描画エリアにすでに描画されているかを判定して、
ビットが立っていない場合は直接書込みます。 ビットが立っている場合は重ね合わせ処理を行います。

書込み時に1,2,3プレーン数ごとに処理を分けます。
1プレーン書き込みを行う時は、BRGプレーンに分けて3色の描画処理。
2プレーン書込みを行う時は、BR,BG,RGの3パターンに分けます。
こうすることで、描画負荷を下げながら色数を増やす事ができます。
3プレーン書込みは、BRGプレーン全部に対して書込みを行うので一番処理が重いですが、
それよりも重ね合わせ処理の方が重いのです。

同時アクセスモードによる消去処理

X1の固有の処理として、同時アクセスモードを使用して3プレーンを同時にクリアできます。
描画時に描画アドレスとタイプをストックして消去に使用します。
描画アドレスをストックしておけば、そこからビットラインバッファアドレスを求める事が出来ます。
消去の際にビットラインバッファをチェックして一度消去した場所はスキップするようにしています。

座標系を固定小数点 9:7 にすることで高速化

X1 SGLは画面サイズは 320x200を使用しています。
横 320は8bitで収まらないため、16bitを使用した場合、計算用に固定小数点 8bitを加えると、24bit必要になります。
そこで、X1 SGL内の座標系は、X座標を 1byteで表現することで高速化を行います。
固定小数点 9:7 の16bitで計算を行い、当たり判定やクリッピングなどを1byteのみで判定しています。

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
XH XH XH XH XH XH XH XH XH XL XL XL XL XL XL XL

0-319の座標は、上位byteでは 0~159で表現されます。
9bit分なので1bitを下位バイトに持たせています。
この方法のメリットは以下の通りです。

  • 24bitではなく16bitの計算で収まる。
  • 表示座標を求める時は、1bit シフトして下位byteの最上位ビットと合わせて行います。
  • 固定小数点が7bit使える。(大抵は 4bitもあれば十分)
  • 移動スピードが2の倍数の時は上位byteのみ計算する。
  • キャラクタ同士やBGの当り判定は 8bitで計算を行って高速化できる。

描画用データ

キャラクタデータは事前にビットシフトを計算して展開します。
容量を多く必要としますが、高速に描画することが可能です。
容量を減らしたい場合は、2ドット単位のデータにしたり、プレーン数を減らす事で調整を行います。

画像の変換は自前のコンバートツールを使っています。
高速化のためにクリップ情報などを事前に計算しています。

コンバート例:

 ; データテーブル
 en1_p0_c2:
  db 0fch ; Pivot(x) -4
  db 0f8h ; Pivot(y) -8
  db 8 ; データ数
  dw en1_p0_c2_0 ; 0
  dw en1_p0_c2_1 ; 1
  dw en1_p0_c2_2 ; 2
  dw en1_p0_c2_3 ; 3
  dw en1_p0_c2_4 ; 4
  dw en1_p0_c2_5 ; 5
  dw en1_p0_c2_6 ; 6
  dw en1_p0_c2_7 ; 7
  
 en1_p0_c2_0:
 ; Xoffset: 0
 ; Mask: [G R]
 ; OutSizeX: 16 OutSizeY: 16
  
  db 183 ; clipy(byte) 200-sizey-1
  db 020h ; DrawType (Plane: RG SizeY: 16)
  db 39 ; clipx(right) 40-sizex+1
  db 63 ; clipx(left) 64-sizex+1
  db 2 ; sizex(byte)
  db 48 ; sizey(pitch)
  
 ; 0
  db 0ffh, 000h, 000h
  db 0ffh, 000h, 000h
  db 0feh, 001h, 000h
  db 0e8h, 017h, 000h
  db 0c8h, 037h, 001h
  db 0c8h, 037h, 000h
  db 085h, 07ah, 010h
  db 082h, 07ch, 011h
  db 0c2h, 03ch, 019h
  db 0c1h, 03eh, 00ch
  db 0e2h, 01dh, 000h
  db 0fch, 003h, 000h
  db 0f0h, 00fh, 002h
  db 0f8h, 007h, 001h
  db 0fch, 003h, 000h
  db 0ffh, 000h, 000h

デモ Balls / GitHub

SGLで16x16スプライトを描画するデモ「Balls」を作ったので、
X1用Ballsの実行ファイル(X1用)を置いておきます。
X1/turbo用なのでX1でも実行できると思います。

youtu.be

2019/07/06 ソースファイルをgithubに公開しました。 github.com

X1 Balls (D88)

性能評価

プレーンの組合せで変わりますが、以下の性能でした。

FPSモード Ball 1プレーン Balls 2プレーン Ball 3プレーン
60 8 6 6
30 17 14 13
20 31 29 19

使用する環境(実機/エミュレータ),X1/turboでも数字が変動します。

プレーン2枚の負荷が相対的に軽いので、利用しやすいと思います。
重ね合わせ処理をもっと細かくチューニングすればもう少し向上できそうですが、
スプライト数が増えてくると、ビットラインバッファの制御負荷をもっと下げる工夫が必要です。

デモ MarkX

X1 SGLを使ったスプライトデモをもう一本作りました。
あんまりリソースを増やさずに作ろうと考えたので、3Dデモになりました。

youtu.be

まとめ/今後の課題

X1 SGLはまだ改善点を探求しています。
ソースを公開しているものの、そもそも使い方を書いてないので、そちらもまとめてみます。

  • より高速な描画や最適化について。
    • より高速化な手法が無いか考え中。制御ワークを使う以外では対象ごとに描画方法を変えるのが一番有効。
  • 左右反転描画手法
    • 処理速度を出来るだけ落とさずに左右反転を行う手法。メモリ軽減に大きく寄与できるのでぜひ欲しい。
  • 上下反転
    • 左右反転と合わせて、メモリ軽減に寄与できます。こちらの方がまだ現実的。
  • プレーン組合せ
    • これはわりと近々に試してみたいネタです。
    • 2プレーン書込みが割と実用的なので、BR,BG,RGを組み合わせても割と高速に表現できるはず。