2024年3月18日月曜日

RP2040-ZERO+gc9a01pyの実描画速度を調べてみる

 gc9a01pyはmicropython派にとって大変有難いライブラリだが、どれくらいの速度で描画できるのかが気になる。

Timerと組み合わせて、簡易的にFPSを調べてみる。

サマリは以下の通り。

関数 1回のdot数 fps
line 240 tft.line(0, 0, 240, i, color565(i,255-i,count&255)) 5fps
line 10 tft.line(115, i, 125, i, color565(i,255-i,count&255)) 106fps
pixcel 1 tft.pixel(30+i, 30+j, color565(i,255-i,j)) 948fps
fill_rect 1 tft.fill_rect(30+i, 30+j, 1, 1, color565(i,255-i,j)) 868fps
fill_rect 100 tft.fill_rect(30+i, 30+j, 10, 10, color565(i,255-i,j)) 755fps
fill_rect 240*240 tft.fill_rect(0, 0, 240, 240, color565(i,255-i,j)) 18fps

line, pixelはdot数律速で、だいたい1秒1000dotが限界。
fill_rectは、SPIの命令送信と液晶内部処理の2つが律速。SPIの命令は最大1000回/s発行できる。
液晶内部での描画は、3パターンという少ない試行数からの試算なので意味があるかは怪しいが、今回の結果からは1dotあたり0.94usかかっていると推測される。
SPIの命令発行にかかる時間が変わらないとして、総塗り替えのときは1dotのときに比べ、54.4ms余分にかかっているので、1dotあたりに直すと0.94usとなる。
この結果を100dotに当てはめると803fpsとなり、実際のfpsに近い値になる。
試してみればいいことではあるが、fill_rectの速度推定にある程度役立つ。

以下は、実際に測定に使ったコードと結果の詳細。

■line:240dot
コードはこのような感じ。

  # Timer、計測部分
  from machine import Timer
  def showFPS(t):
      global count, count_prev, tft
      fps = count - count_prev
      tft.text(font,"FPS: {0}".format(fps),40,120,WHITE,BLACK)
      count_prev = count
  
  tim = Timer(period=1000, mode=Timer.PERIODIC, callback=showFPS)
  # 描画部分
  count = 0
  count_prev = 0
  while True:
      for i in range(240):
          count += 1
          tft.line(0, 0, 240, i, color565(i,255-i,count&255))
この例は、直線描画の速度を調べている。
結果、ほぼ5fps。ごくまれに6になるときもあるが、無視してよいレベル。


■line:10dot
今度は、線分の式を「tft.line(115, i, 125, i, color565(i,255-i,count&255))

」に変えてみる。
結果、106fpsくらいまで性能向上する。

lineの中身を見てみるとわかるが、これって結局pixelで1dotずつ描画指令を送信しているので、pixel数の多さで速度が決まるということだ。
このライブラリに限らず、なるべく1点ずつ打つのを避けないと、どんどん描画速度が下がってしまう。

■pixel
じゃあ、今度は1秒に何点打てるのか?を測ってみる。
while True:
    for i in range(180):
        for j in range(180):
            count += 1
            tft.pixel(30+i, 30+j, color565(i,255-i,j))
見てみると、948点/s行けるようだ。
lineで10点ずつ打つと100fps→1000点/sで、240点ずつだと5fps→1200点/sなので、lineの結果ともマッチする。



■fill_rect:1dot
次は、fill_rectを試す。vline, hline, rectも内部でこの式を呼び出している。
fill_rectは、液晶に対し開始終了地点と色を送るだけなので、実はpixelに比べてSPIで送る指示は2byteしか変わらず、送信回数は同じ。
もしSPIの送信がボトルネックとすれば、pixelと遜色ない速度が出せるはず。
while True:
    for i in range(180):
        for j in range(180):
            count += 1
            tft.fill_rect(30+i, 30+j, 1, 1, color565(i,255-i,j))
結果、868fps前後となった。pixelに比べ、8.4%の性能低下で済んでいる。
これは1点ずつの描画だが、次は1回の描画面積が広くなっても変わらないか、試してみる。

■fill_rect:100dot
縦横を10ずつにして、1度に10dotずつ描画させてみる。
while True:
    for i in range(180):
        for j in range(180):
            count += 1
            tft.fill_rect(30+i, 30+j, 10, 10, color565(i,255-i,j))
この場合は755 or 761fpsとなった。RP2040⇔GC9A01の間のSPIは変わらないが、液晶内部の描画が増えた分でこれだけ遅くなったということ。とはいえ13%の性能低下で、1回で更新できるdot数ベースならlineに比べて圧倒的に速い。

最後に、240x240全て塗り替えにすると画像は無いが18fpsくらいだった。さすがに全消去は遅い。

以上。

0 件のコメント:

コメントを投稿