0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

70歳の挑戦... Arduino IDEのコンパイルオプション -Ofast でマンデルブロー集合描画高速化しました

Posted at

 前回の「マンデルブロー集合の描画高速化」から更なる高速化(20枚描画に49秒)が出来ました。
8Para_49s.jpg

 次の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 は次のように設定しました。
ESPS3_Tools.jpg
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) {
}

 最後まで見ていただきありがとうございました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?