[i.MXRT1060]emWinでテトリスを作ってみた〜第3回〜


さて、前回はテトリミノの描画に時間が掛かりすぎてゲームにならないという失敗をしました。

今回は、もっと高速にテトリミノを描画する方法を探ります。

テトリミノ描画高速化の方法は?

考えたことは、いくつかあります。

1)BitmapイメージをDTC-RAMに配置してアクセスを高速化してみる
2)1)に加えてdisplay()関数をITC-RAMに置いてアクセスを高速化してみる
3)そもそもemWimで用意されている描画APIで四角形を描画する

以上です。

ま〜まだまだ方法はあるのでしょうが、ここでは、この3つを試してみます。

では、早速1)からやって行きましょう。

BitmapイメージをDTC-RAMに配置してアクセスを高速化してみる

i.MXRT1060はフラッシュレスで、外部QSPIフラッシュやHyper Flashから直接実行するアーキテクチャです。

実行スピードが重要なアプリケーションは、内部TCMメモリやOCRAM(On-Chip-RAM)、あるいはSDRAMなどに一旦ダウンロードしてRAMから実行することで高速に実行することが出来ます。

ということで、外部フラッシュに配置されているBitmapイメージを内部DTC-RAMに配置して高速にデータにアクセスできるようにしてみます。

とりあえずグレー色のテトリミノだけをDTC-RAMに配置して確認します。

これは簡単です。

  1. まず、用意したグレーのテトリミノのBitmapデータをコンバートする際に、セクション名を.dataに変更します。
  2. 次に、MCUXPresso IDEのデータセクション配置をDTCに変更して、再ビルドして完了

最初にBitmapデータをobjcopyで変換したときは、セクション名をわざわざ.rodataに変更していました。これを元に戻すだけです。デフォルトは.dataセクションのようです。

セクション名を.dataに変更

念のため下記コマンドで再度変換しておきます。

$./objcopy -I binary -B arm -O elf32-littlearm gray_20.bmp gray_20_ram.o

同じBitmapファイルを変換していますが、出力のファイル名に_ramを追加しました。このBitmapイメージへのアクセスは、前のもの(_binary_gray_20_bmp_start[])と変わりませんので、ソースコードの修正は必要ありません。

念のため、出力されたgray_20_ram.oの中身をreadelfで覗いてみます。

readelfで確認
readelfで確認

そして、MCUXPresso IDEのプロジェクトにドラッグ&ドロップしてコピーします。

MCUXpresso にコピーする
MCUXpresso にコピーする
注意!
この時、.dataセクションで作成したBitmapイメージは、.rodataの物とBitmapイメージへのアドレスは変わらないため、複数定義でエラーが発生してしまいます。.rodataセクションのBitmapイメージファイルは削除しておきます。

MCUXPresso IDEのプロパティ変更

プロジェクトプロパティ内のデフォルトデータセクションの配置をDTC-RAMに変更することで、今回作成した.dataセクションのBitmapイメージデータは、自動的にDTC-RAMに配置されることになります。

プロパティ設定
プロパティ設定

ビルドしてデバッグ開始

リンカーのマップファイル内容
リンカーのマップファイル内容

ビルドしてみると、確かにDTC RAMに配置されているのが確認出来ました。

早速、実行してみます。

あれ?あんまり変わんない?

やはり、2秒ちょっとは掛かってます。

ん〜〜〜ダメです。

display()関数をITC-RAMに置いてアクセスを高速化してみる

では、次に諦めずに、display()関数もDTC RAMに配置してRAMから実行してみたらどうでしょうか?

これも簡単に試してみることが可能です。

GCCの属性機能を使ってセクション指定することで、簡単にRAMから関数を実行することが可能です。

以前の記事も参考にしてください。以前の記事はこちらから→

display()関数の前に以下のようなおまじないをすることで、指定のセクションに配置でき、自動的にFlashメモリ領域からRAM領域にコピーしてくれます。

void __attribute__((section(“.data.$RAM4”))) display(){ 関数の中身}

ビルド後のメモリマップ

セクション配置をITCに変更したdisplay()関数
セクション配置をITCに変更したdisplay()関数

確かに、ITC-RAM(0x0000_0000〜)に配置されています。

実行してみる

ん〜〜やっぱり、描画速度が速くなりませんね。無理なんでしょうか?

デバッグを開始したと同時に描画されないと、テトリミノが積み上がってしまうと必ず大きな遅延になってしまいます。

これでは、ダメです。

残念ですが、自分で作ったテトリミノ(Bitmapイメージ)は諦めます。

そもそもemWimで用意されている描画APIで四角形を描画する

次に、そもそもなんですが、、、emWinのGUI部品や基本図形描画APIがあって、そっちを使う方が単純だし、速いんじゃないか?と。。。

テトリミノは単純なブロックだし、自分で絵を描く必要はないんです。そうです。

最初からそうしていれば良かったんです。

で、早速emWinの描画APIを確認したところ、以下のようなAPIがあります。

void GUI_FillRect(int x0, int y0, int x1, int y1)

このAPIは、色付きの四角形を指定した場所、大きさで表示してくれるAPIです。

色の設定は、事前にGUI_SetColor() APIで指定しておくとその色で四角形を描画してくれます。

早速、やってみます。

 

void display(){
	//utickIntFlag = false;
	/* Screen clear  */
	//PRINTF("\033[2J");   	//Escape Sequence Clear screen
	//system("cls");
	//PRINTF("\033[1;1f");	//Cursor moves to (1,1)
	memcpy(dispBuffer, field, sizeof(field));
	for (int y =0; y< MINO_HEIGHT; y++)
		for(int x =0; x < MINO_WIDTH; x++)
			dispBuffer[minoY +y][minoX+x] |= minoShapes[minoType][minoAngle][y][x];/* Drawing */
	for (int y =0; y < FIELD_HEIGHT; y++){
		for(int x=0; x<FIELD_WIDTH; x++){
			
		    if (dispBuffer[y][x]){
		        //PRINTF("");
                        
                        /*GUI_FillRect()に変えてみる*/
                        //GUI_BMP_Draw(gray, y * cell_ySize, x * cell_xSize);
                        /*色をグレーに設定*/
                        GUI_SetColor(GUI_GRAY_55);
                        /*四角形を描画*/
                        GUI_FillRect(y * cell_ySize, x * cell_xSize, (y * cell_ySize) + cell_ySize -2 , (x * cell_xSize) + cell_xSize -);

		    }else{
		        //PRINTF("  ");
		    }

		}
	    //PRINTF("\n\r");
	}
}

これでビルドして実行してみます。
どうでしょうか?
描画が速いですよね。

グレーのテトリミノの描画
グレーのテトリミノの描画

デバッグを開始したと同時に描画される感じです。これです!これ!

このくらいの速さで描画されないとゲームになりません。

良かった。良かった。

あとは、このテトリミノが1秒毎に上から落ちてきたり、キーボードの右左の操作を加えて行きます。

今回は、ここまでにして、次回にテトリスを完成させて行きたいと思います。

お楽しみ。