RX72N Envision Kit でダブルバッファとTinyGLの実験

ダブルバッファ

RX72N では、内蔵メモリが 512K + 512K と増えたので、480x272 の 16 ビットカラーで、ダブルバッファを使う事が出来る。

ダブルバッファは、デジタルストレージオシロスコープで波形をレンダリングするのに都合が良いので、実験する必要性があった。

RX65N では、フレームバッファが1枚分しかなく、16 ビットカラーでは、ダブルバッファにする事が出来ない。
※ 8 ビットカラーなら可能かもしれないが、ラインアドレスは 64 バイトの倍数にする制約があり変則的な設定となる。

描画オブジェクトが GUI のような性質ならまだやりようもあるが、ダイナミックな描画をしようとすると、どうしても、ダブルバッファが必要となる。
※描画速度を気にしなければ、他にも方法はある・・

ダブルバッファの描画では、毎フレーム以下の手順で描画を行う。

  • 表示フレーム同期
  • B のバッファを表示用にする
  • A のバッファを全面クリア
  • A のバッファに全てのオブジェクトを描画
  • A、B を入れ替える

簡単には以上の手順を繰り返す。

通常、全面クリア+全てのオブジェクト描画が、1フレーム(16.6ミリ秒)以内に収まれば、フルフレーム(60Hz)で遅延なくスムーズに描画出来る。
描画オブジェクトが物理的に増えて、1フレームに収まらないと、フレームレートはどんどん落ちていく。
それでも、描画は裏で行っており、見えないので、チラツキは出ない。

glcdc_mgr は、フレームバッファの先頭アドレスを管理している。
GLCDC ハードウェアーは、水平ラインのアドレスは64の倍数でなければならない・・
ダブルバッファが有効な時、ページフリップに従ったアドレスを戻す。

        static const int16_t line_width =
            (((width * static_cast<int16_t>(PXT) / 8) + 63) & 0x7fc0) / (static_cast<int16_t>(PXT) / 8);
        static const uint32_t frame_size =
            line_width * (static_cast<uint32_t>(PXT) / 8) * height;

        void* get_fbp() const noexcept
        {
            uint32_t ofs = 0;
            if(enable_double_) {
                ofs = (flip_count_ & 1) != 0 ? 0 : frame_size;
            }
            if(layer2_org_ != nullptr) {
                uint32_t org = reinterpret_cast<uint32_t>(layer2_org_);
                return reinterpret_cast<void*>(org + ofs);
            } else if(layer1_org_ != nullptr) {
                uint32_t org = reinterpret_cast<uint32_t>(layer1_org_);
                return reinterpret_cast<void*>(org + ofs);
            }
            return nullptr;
        }

垂直同期のタイミングで先頭アドレス(GR2FLM2)がロードされる。
※「GR2VEN」を有効にしておかないと、レジスタを書き換える事が出来ない。

        void sync_vpos() const noexcept
        {
            if(enable_double_) {
                uint32_t ofs = (flip_count_ & 1) != 0 ? 0 : frame_size;
                uint32_t org = reinterpret_cast<uint32_t>(layer2_org_);
                GLC::GR2VEN = 1;
                GLC::GR2FLM2 = org + ofs;
            }
            volatile auto n = ctrl_blk_.vpos_count;
            while(n == ctrl_blk_.vpos_count) {
                asm("nop");
            }
        }

また、RX651/RX65N から搭載された描画エンジン DRW2D は、ソフトの描画に比べて、格段に高速なので、これを有効に活用するのに適している。


OpenGL 的なフレームワーク

「ダブルバッファ」と言えば、やはり3Dグラフィックスだろうと思う。
※スプライトと言う人もいるが・・・

昔から、リアルタイム 3D グラフィックスには、注力しているので、最も馴染みが深い OpenGL を縮小にした API を実装して実験してみた。

とりあえず、L チカに相当するキューブの回転w

RX マイコン内蔵の DRW2D 描画エンジンは、2D の描画用だが、基本的な構造が、3D にもマッチするように構成されている。

オブジェクトの座標をマトリックス演算して、透視変換、スクリーン座標変換まで行えば、2D 座標となり、そのまま描画出来る。
※テクスチャマッピングは多少難解かもしれないが、透視変換に適した API があるようだ。(これは今後の課題)

ポリゴンの描画では、裏と表の概念があり、親和性が良い。
※殆ど何もする事が無いと言いたいところだけど、立体ボリュームに対するクリッピング、ライティングなどを考えると、ちゃんと実装するのはかなり大変ではある。
端折ったテスト的なレンダリングなら簡単!

ほぼ OpenGL と同じような手順で描画出来るようにした、C++ なので、色々と便利機能を実装出来る。

    float ax = 0.0f;
    float ay = 0.0f;
    while(1) {
        render_.sync_frame();

        render_.clear(DEF_COLOR::Black);

        auto& m = tgl_.at_matrix();
        m.set_viewport(0, 0, LCD_X, LCD_Y);
        m.set_mode(gl::matrixf::mode::modelview);
        m.identity();
        m.translate(0.0f, 0.0f, -10.0f);
        m.rotate(ax, 1.0f, 0.0f, 0.0f);
        m.rotate(ay, 0.0f, 1.0f, 0.0f);

        ax += 1.0f;
        if(ax >= 360.0f) ax -= 360.0f;
        ay += 1.5f;
        if(ay >= 360.0f) ay -= 360.0f;

        m.set_mode(gl::matrixf::mode::projection);
        m.identity();
        m.perspective(45.0f, static_cast<float>(LCD_X) / static_cast<float>(LCD_Y), 1.0f, 50.0f);

//      draw_box_(2.0f, TGL::PTYPE::LINE_LOOP);
        draw_box_(2.0f, TGL::PTYPE::QUAD);

        tgl_.renderring();
    }

RX72N TFU を有効に!

RX72N は、三角関数演算器を持っているので、これを有効に利用するコードを追加。

回転行列の演算では、以下の API を利用しているので、その部分で、専用 API を呼ぶようにした。

    static inline void deg_sin_cos_(float deg, float& si, float& co) noexcept
    {
#ifdef SIG_RX72N
        __builtin_rx_sincosf(deg  * vtx::deg2rad_f_, &si, &co);
#else
        si = std::sin(deg * vtx::deg2rad_f_);
        co = std::cos(deg * vtx::deg2rad_f_);
#endif
    }

むすび

非常に初歩的ではあるが、3D オブジェクトの描画をテストした。
実用的に使うには、まだまだこれから色々入れないとならない・・・

とりあえず、ダブルバッファの実験から脱線したので、このプロジェクトはストールして、デジタルストレージオシロスコープの実装に戻る。

DRW2D エンジンは、今までほとんど使って来なかったので、良いきっかけになったと思う。

DRW2D エンジンは、アンチエリアスもサポートされており、このハードウェアーの情報は少ないが、かなり良く出来ていると思う。