前回の「マンデルブロー集合の描画高速化」から更なる高速化(20枚描画に49秒)が出来ました。
次の2点の効果が大きいです。
1.ピクセル情報保存用に、大サイズの配列 cels[480][320] が作成できたこと
2.オプション -Ofast でコンパイルできたこと
1は以前メモリー不足で不可能だったものが、今回使用している「ESP32-S3」のフラッシュメモリーサイズが 16MB であり可能のようです。
2のオプションについては
-O2 可
-O3 不可
-Ofast 可
という結果でした。私の環境ではこの設定は次のようにしました。Arduino IDE のフォルダー
C:\Users\(ユーザー名)\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.0.2
内の platform.txt をもとに次のファイルを作成・編集し、同フォルダーに保存します。再度 Arduino IDEを立ち上げれば反映されます。
platform.local.txt
---
# EXPERIMENTAL feature: optimization flags
# - this is alpha and may be subject to change without notice
compiler.optimization_flags=-Ofast
compiler.optimization_flags.release=-Ofast
compiler.optimization_flags.debug=-Og -g3
---
使用機材の構成は「マンデルブロー集合の描画高速化」をご覧ください。
Arduino IDE 2.3.2 での Tools は次のように設定しました。
LovyanGFXライブラリーで必要な myLovyanGFX.hpp です。スケッチと同じフォルダーに保存します。
myLovyanGFX.hpp
#pragma once
#define LGFX_USE_V1
#include <LovyanGFX.hpp>
class LGFX : public lgfx::LGFX_Device
{
lgfx::Panel_ILI9488 _panel_instance;
lgfx::Bus_Parallel8 _bus_instance; // 8ビットパラレルバスのインスタンス (ESP32のみ)
public:
LGFX(void) {
{ // バス制御の設定を行います。
auto cfg = _bus_instance.config();// バス設定用の構造体を取得します。
// 8ビットパラレルバスの設定
cfg.freq_write = 20000000; // 送信クロック (最大20MHz, 80MHzを整数で割った値に丸められます)
cfg.pin_wr = 11; // WR を接続しているピン番号
cfg.pin_rd = 12; // must // RD を接続しているピン番号
cfg.pin_rs = 10; // RS(D/C)を接続しているピン番号
cfg.pin_d0 = 18; // D0を接続しているピン番号
cfg.pin_d1 = 37; // D1を接続しているピン番号
cfg.pin_d2 = 17; // D2を接続しているピン番号
cfg.pin_d3 = 38; // D3を接続しているピン番号
cfg.pin_d4 = 16; // D4を接続しているピン番号
cfg.pin_d5 = 39; // D5を接続しているピン番号
cfg.pin_d6 = 15; // D6を接続しているピン番号
cfg.pin_d7 = 40; // D7を接続しているピン番号
_bus_instance.config(cfg); // 設定値をバスに反映します。
_panel_instance.setBus(&_bus_instance); // バスをパネルにセットします。
}
{ // 表示パネル制御の設定を行います。
auto cfg = _panel_instance.config(); // 表示パネル設定用の構造体を取得します。
cfg.pin_cs = 9; // 27 CSが接続されているピン番号 (-1 = disable)
cfg.pin_rst = 8; // 33 RSTが接続されているピン番号 (-1 = disable)
cfg.pin_busy = -1; // BUSYが接続されているピン番号 (-1 = disable)
cfg.memory_width = 320; // ドライバICがサポートしている最大の幅
cfg.memory_height = 480; // ドライバICがサポートしている最大の高さ
cfg.panel_width = 320; // 実際に表示可能な幅
cfg.panel_height = 480; // 実際に表示可能な高さ
cfg.offset_x = 0; // パネルのX方向オフセット量
cfg.offset_y = 0; // パネルのY方向オフセット量
cfg.offset_rotation = 0; // 回転方向の値のオフセット 0~7 (4~7は上下反転)
cfg.dummy_read_pixel = 8; // ピクセル読出し前のダミーリードのビット数
cfg.dummy_read_bits = 1; // ピクセル以外のデータ読出し前のダミーリードのビット数
cfg.readable = true; // データ読出しが可能な場合 trueに設定
cfg.invert = false; // パネルの明暗が反転してしまう場合 trueに設定
cfg.rgb_order = false; // パネルの赤と青が入れ替わってしまう場合 trueに設定
cfg.dlen_16bit = false; // 16bitパラレルやSPIでデータ長を16bit単位で送信するパネルの場合 trueに設定
cfg.bus_shared = false; // SDカードとバスを共有している場合 trueに設定(drawJpgFile等でバス制御を行います)
_panel_instance.config(cfg);
}
setPanel(&_panel_instance); // 使用するパネルをセットします。
}
};
バージョンアップしたスケッチ本体です。
ESP_S3_Mandelbrot_8Para_2.ino
// Global variables
// use 174940 bytes (53%) of dynamic memory,
// leaving 152740 bytes for local variables.
// Maximum is 327680 bytes.
// this takes 49 seconds.
#include "myLovyanGFX.hpp"
static LGFX tft; // LGFXのインスタンスを作成。
const uint16_t col[]{
0x0000, //0
0x2228, //1
0x867E, //2LightSkyBlue
0xFF1B, //3MistyRose
0x480F, //4Indigo
0xAEBC, //5LightBlue
0xF81F, //6Magenta
0xFFFB, //7LightYellow
0x07FF, //8Cyan
0xF800, //9Red
0x07E0, //10Green
0x001F, //11Blue
0xFCEE, //12LightSalmon
0xEF31, //13Khaki
0xDFFF, //14LightCyan
0xFFE0 //15Yellow
};
#define W 480// tft.width()
#define H 320// tft.heght()
#define X0 240//tft.width()/2
#define Y0 160//tft.height()/2
const float toRad = PI / 180.0;//度をラジアンに変換
const float absZ = 4.0; // 発散判定
const uint16_t page = 20;
const uint16_t dp = 2;
// default Mandelbrot Set -----------------------------------------
// viewpW=2.4; CtoX = 0.0; CtoY = 0.0;
// repeat=200; page=1; dp=0;
// ----------------------------------------------------------------
// viewpW = 0.00024; // 10000倍に拡大
// Viewport width; Viewport Center CtoX;CtoY;
// 風景の良い場所例
// example 1
// viewpW = 0.00024; CtoX = -1.24997; CtoY = -0.00877;
// repeat=190; page=28; dp=2;
// example 2
// viewpW = 0.00024; CtoX=-1.401;CtoY=-0.000178;
// repeat=300; page=19; dp=2;
const float viewpW = 0.0012; // viewport width(window width)
const float di = viewpW / 480.0;// real/imaginary calculation step
// 1 pixel in Real and Imaginary number
const float CtoX = -1.19998; // 現在の表示(viewpW)の中心
const float CtoY = -0.15507;
uint16_t repeat = 120; // 反復上限、page進行に応じ増加させる
uint16_t k; // 反復回
float x, y, zr, zi, R, I;//複素計算変数
uint16_t C[800]; // 色配列
uint32_t now, starttime;
uint16_t pi;
int16_t j,i;
uint16_t Xi,Yj;
bool cels[W][H]; // 各セルの発散状況記憶
void setup() {
Serial.begin(115200);
Serial.println("begin");
tft.init();
tft.setRotation(1);
for(uint16_t l=0;l<W;l++){
for(uint16_t m=0;m<H;m++) cels[l][m]=false;
}
for(uint16_t n = 0; n < 800; n++) C[n] = ColorSel(n);
starttime=millis();
tft.startWrite(); // SPIでなくても高速化に効果ありかも
for (pi = 1; pi <= page; pi++) {// 反復数上限を増やしていく
Mandelbrot();
now = millis();
tft.setCursor(0,0);
tft.printf("%2dpage", pi);
tft.setCursor(0,8);
tft.printf("%.0fs", (now - starttime)*0.001);
repeat += dp * pi;
}
grid();
}
void Mandelbrot(void) {
for ( j = - Y0; j < Y0; j++) {
Yj = Y0-j-1;
y = j * di + CtoY;
for ( i = - X0; i < X0; i++) {
Xi = X0 + i;
if(cels[Xi][Yj]==false){ // 未発散のピクセル(黒)のみ計算
x = i * di + CtoX;
zr = 0.0;
zi = 0.0;
k = 0;//-1;
do {
k++;
R = zr * zr - zi * zi + x;
I = 2 * zr * zi + y;
zr = R;
zi = I;
if ( k == repeat ) break;
} while ( (zr * zr + zi * zi) < absZ );
if (k < repeat) {// 新たに発散したピクセルのみ描画
tft.drawPixel(Xi, Yj, C[k]);// 0x00F +0x0100*k+ 2*k);
cels[Xi][Yj]=true;
}
}
}
}
}
void grid(void) {
tft.drawLine(X0 + 100, 0, X0 + 100, tft.height(), 0xFFFF);
tft.drawLine(X0 - 100, 0, X0 - 100, tft.height(), 0xFFFF);
tft.drawLine(X0 + 200, 0, X0 + 200, tft.height(), 0xFFFF);
tft.drawLine(X0 - 200, 0, X0 - 200, tft.height(), 0xFFFF);
tft.drawLine( 0, Y0 + 100, tft.width(), Y0 + 100, 0xFFFF);
tft.drawLine( 0, Y0 - 100, tft.width(), Y0 - 100, 0xFFFF);
tft.drawLine(X0, 0, X0, tft.height(), 0xFFFF); // 中心線
tft.drawLine(0, Y0, tft.width(), Y0, 0xFFFF);
tft.drawString("1.0",X0+192,H-8);
tft.drawString("0",0,Y0-3);
tft.drawString("0",X0-2,H-8);
tft.drawString("-1.0",X0-213,H-8);
tft.drawString("End",0,H-8);
}
uint16_t ColorSel(uint16_t c) {
uint16_t re, gr, bl; // color 0-255
if (c < 16) return col[c];
else {
c -= 16;
re = 128 + 126 * cos(c * toRad);
gr = 128 + 126 * cos((c + 120) * toRad);
bl = 128 + 126 * cos((c + 240) * toRad);
return tft.color565(re, gr, bl);
}
}
void loop(void) {
}
最後まで見ていただきありがとうございました。