Kami memahami fitur dari subsistem grafis mikrokontroler

Halo!



Pada artikel ini, saya ingin berbicara tentang fitur-fitur penerapan antarmuka pengguna grafis dengan widget pada mikrokontroler dan cara memiliki antarmuka pengguna yang sudah dikenal dan FPS yang layak. Saya ingin fokus bukan pada pustaka grafis tertentu, tetapi pada hal-hal umum - memori, cache prosesor, dma, dan sebagainya. Karena saya adalah pengembang tim Embox , contoh dan eksperimen akan ada di OS RT ini.





Sebelumnya kita sudah berbicara tentang menjalankan pustaka Qt pada mikrokontroler . Animasi ternyata cukup mulus, tetapi biaya memori bahkan untuk menyimpan firmware cukup signifikan - kode dijalankan dari memori flash QSPI eksternal. Tentu saja, ketika diperlukan antarmuka yang kompleks dan multifungsi, yang juga mengetahui cara melakukan beberapa jenis animasi, maka biaya sumber daya perangkat keras dapat dibenarkan (terutama jika Anda sudah mengembangkan kode ini untuk Qt).



Tetapi bagaimana jika Anda tidak membutuhkan semua fungsi Qt? Bagaimana jika Anda memiliki empat tombol, satu kontrol volume dan beberapa menu popup? Pada saat yang sama, saya ingin "terlihat bagus dan bekerja cepat" :) Maka disarankan untuk menggunakan alat yang lebih ringan, misalnya, pustaka lvgl atau yang serupa.



Dalam proyek Embox kami, Nuklear telah di- porting beberapa waktu lalu - sebuah proyek untuk membuat perpustakaan yang sangat ringan yang terdiri dari satu tajuk dan memungkinkan Anda membuat GUI sederhana dengan mudah. Kami memutuskan untuk menggunakannya untuk membuat aplikasi kecil di mana akan ada widget dengan satu set elemen grafis dan yang dapat dikontrol melalui layar sentuh.



STM32F7-Discovery dengan Cortex-M7 dan layar sentuh dipilih sebagai platform.



Pengoptimalan pertama. Hemat memori



Jadi, pustaka grafis dipilih, begitu juga platformnya. Sekarang mari kita pahami apa saja sumber daya itu. Perlu dicatat di sini bahwa SRAM memori utama berkali-kali lebih cepat daripada SDRAM eksternal, jadi jika ukuran layar memungkinkan Anda, maka tentu saja lebih baik meletakkan framebuffer di SRAM. Layar kami memiliki resolusi 480x272. Jika kita menginginkan warna 4 byte per piksel, maka kita mendapatkan sekitar 512 KB. Pada saat yang sama, ukuran RAM internal hanya 320 dan langsung terlihat jelas bahwa memori video akan menjadi eksternal. Pilihan lainnya adalah mengurangi kedalaman bit warna menjadi 16 (yaitu 2 byte), dan dengan demikian mengurangi konsumsi memori menjadi 256 KB, yang sudah dapat masuk ke dalam RAM utama.



Hal pertama yang dapat Anda coba adalah menghemat segalanya. Mari buat buffer video 256 KB, tempatkan di RAM dan gambar ke dalamnya. Masalah yang langsung kami temui adalah "kedipan" pemandangan yang terjadi saat menggambar langsung ke memori video. Nuklear menggambar ulang seluruh adegan dari awal, jadi setiap kali seluruh layar diisi terlebih dahulu, lalu widget digambar, kemudian sebuah tombol dimasukkan ke dalamnya, tempat teks ditempatkan, dan seterusnya. Hasilnya, dengan mata telanjang dapat terlihat bagaimana seluruh pemandangan digambar ulang dan gambar "berkedip". Artinya, penempatan sederhana di memori internal tidak menghemat.



Buffer menengah. Pengoptimalan kompiler. FPU



Setelah kami mengotak-atik metode sebelumnya (penempatan di memori internal) sebentar, ingatan X Server dan Wayland segera mulai muncul di benak. Ya, pada kenyataannya, pengelola jendela terlibat dalam memproses permintaan dari klien (hanya aplikasi khusus kami), dan kemudian mengumpulkan elemen ke dalam adegan terakhir. Misalnya, kernel Linux mengirimkan peristiwa dari perangkat input ke server melalui driver evdev. Server, pada gilirannya, menentukan klien mana yang akan menangani acara tersebut. Klien, setelah menerima sebuah acara (misalnya, menekan pada layar sentuh), menjalankan logika internal mereka - mereka menyorot tombol, menampilkan menu baru. Selanjutnya (sedikit berbeda untuk X dan Wayland) baik klien itu sendiri atau server menarik perubahan ke buffer. Dan kemudian penyusun menyusun semua bagian untuk digambar ke layar.Penjelasan yang cukup sederhana dan skematis di sinidisini .



Menjadi jelas bahwa kami membutuhkan logika serupa, tetapi kami benar-benar tidak ingin mendorong X Server ke stm32 demi aplikasi kecil. Karena itu, mari kita coba menggambar bukan di memori video, tetapi di memori biasa. Setelah merender seluruh adegan, itu akan menyalin buffer ke memori video.



Kode widget
        if (nk_begin(&rawfb->ctx, "Demo", nk_rect(50, 50, 200, 200),
            NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|
            NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) {
            enum {EASY, HARD};
            static int op = EASY;
            static int property = 20;
            static float value = 0.6f;

            if (mouse->type == INPUT_DEV_TOUCHSCREEN) {
                /* Do not show cursor when using touchscreen */
                nk_style_hide_cursor(&rawfb->ctx);
            }

            nk_layout_row_static(&rawfb->ctx, 30, 80, 1);
            if (nk_button_label(&rawfb->ctx, "button"))
                fprintf(stdout, "button pressed\n");
            nk_layout_row_dynamic(&rawfb->ctx, 30, 2);
            if (nk_option_label(&rawfb->ctx, "easy", op == EASY)) op = EASY;
            if (nk_option_label(&rawfb->ctx, "hard", op == HARD)) op = HARD;
            nk_layout_row_dynamic(&rawfb->ctx, 25, 1);
            nk_property_int(&rawfb->ctx, "Compression:", 0, &property, 100, 10, 1);

            nk_layout_row_begin(&rawfb->ctx, NK_STATIC, 30, 2);
            {
                nk_layout_row_push(&rawfb->ctx, 50);
                nk_label(&rawfb->ctx, "Volume:", NK_TEXT_LEFT);
                nk_layout_row_push(&rawfb->ctx, 110);
                nk_slider_float(&rawfb->ctx, 0, &value, 1.0f, 0.1f);
            }
            nk_layout_row_end(&rawfb->ctx);
        }
        nk_end(&rawfb->ctx);
        if (nk_window_is_closed(&rawfb->ctx, "Demo")) break;

        /* Draw framebuffer */
        nk_rawfb_render(rawfb, nk_rgb(30,30,30), 1);

        memcpy(fb_info->screen_base, fb_buf, width * height * bpp);




Contoh ini membuat jendela 200 x 200 px dan menggambar grafik ke dalamnya. Adegan terakhir itu sendiri ditarik ke buffer fb_buf, yang kami alokasikan ke SDRAM. Dan kemudian di baris terakhir, memcpy dipanggil. Dan semuanya berulang dalam siklus tanpa akhir.



Jika kita hanya membangun dan menjalankan contoh ini, kita mendapatkan sekitar 10-15 FPS. Yang pasti tidak terlalu bagus, karena bisa terlihat bahkan dengan mata. Selain itu, karena kode render Nuklear berisi banyak kalkulasi floating point, kami mengaktifkan dukungannya pada awalnya , tanpanya FPS akan menjadi lebih rendah. Pengoptimalan (gratis) pertama dan paling sederhana tentu saja adalah tanda kompilator -O2.



Mari buat dan jalankan contoh yang sama - kita mendapatkan 20 FPS. Lebih baik, tapi masih belum cukup untuk mendapatkan pekerjaan yang bagus.



Mengaktifkan cache prosesor. Mode Write-Through



Sebelum beralih ke pengoptimalan lebih lanjut, saya akan mengatakan bahwa kita menggunakan plugin rawfb sebagai bagian dari Nuklear, yang menarik langsung ke memori. Karenanya, pengoptimalan memori terlihat sangat menjanjikan. Hal pertama yang terlintas dalam pikiran adalah cache.



Di versi Cortex-M yang lebih lama, seperti Cortex-M7 (kasus kami), cache prosesor tambahan (cache instruksi dan cache data) sudah terpasang. Ini diaktifkan melalui register CCR dari System Control Block. Tetapi dengan dimasukkannya cache, masalah baru datang - ketidakkonsistenan data dalam cache dan memori. Ada beberapa cara untuk mengelola cache, tetapi dalam artikel ini saya tidak akan membahasnya, jadi saya akan beralih ke salah satu yang paling sederhana, menurut saya. Untuk mengatasi masalah ketidakkonsistenan cache / memori, kita cukup menandai semua memori yang tersedia sebagai "non-cacheable". Artinya, semua penulisan ke memori ini akan selalu masuk ke memori dan bukan ke cache. Tetapi jika kita menandai semua memori dengan cara ini, maka tidak akan ada gunanya cache juga. Ada pilihan lain. Ini adalah mode "lewati", di mana semua penulisan ke memori yang ditandai sebagai tulis dikirim secara bersamaan ke cache,dan dalam ingatan. Hal ini menyebabkan overhead penulisan, tetapi di sisi lain, sangat mempercepat pembacaan, sehingga hasilnya akan bergantung pada aplikasi tertentu.



Untuk Nuklear, mode tulis-tayang ternyata sangat bagus - kinerjanya naik dari 20 FPS menjadi 45 FPS, yang dengan sendirinya sudah cukup bagus dan mulus. Efeknya tentu menarik, kami bahkan mencoba menonaktifkan mode write through, tidak memperhatikan inkonsistensi data, tetapi FPS hanya naik menjadi 50 FPS, artinya, tidak ada peningkatan yang signifikan dibandingkan dengan write through. Dari sini kami menyimpulkan bahwa aplikasi kami membutuhkan banyak operasi baca, bukan menulis. Pertanyaannya tentu saja dimana? Mungkin karena banyaknya transformasi dalam kode rawfb, yang sering mengakses memori untuk membaca koefisien berikutnya atau semacamnya.



Buffer ganda (sejauh ini dengan buffer perantara). Mengaktifkan DMA



Saya tidak ingin berhenti di 45 FPS, jadi kami memutuskan untuk bereksperimen lebih lanjut. Ide selanjutnya adalah buffering ganda. Idenya dikenal luas, dan, secara umum, sederhana. Kami menggambar pemandangan menggunakan satu perangkat ke satu buffer, sementara perangkat lain menampilkan dari buffer lain. Jika Anda melihat kode sebelumnya, Anda dapat dengan jelas melihat loop di mana adegan pertama kali ditarik ke buffer, dan kemudian isinya disalin ke dalam memori video menggunakan memcpy. Jelas bahwa memcpy menggunakan CPU, yaitu rendering dan penyalinan terjadi secara berurutan. Ide kami adalah bahwa penyalinan dapat dilakukan secara paralel menggunakan DMA. Dengan kata lain, saat prosesor menggambar adegan baru, DMA menyalin adegan sebelumnya ke memori video.



Memcpy diganti dengan kode berikut:



            while (dma_in_progress()) {
            }

            ret = dma_transfer((uint32_t) fb_info->screen_base,
                    (uint32_t) fb_buf[fb_buf_idx], (width * height * bpp) / 4);
            if (ret < 0) {
                printf("DMA transfer failed\n");
            }

            fb_buf_idx = (fb_buf_idx + 1) % 2;


Di sini fb_buf_idx dimasukkan - indeks buffer. fb_buf_idx = 0 adalah buffer depan, fb_buf_idx = 1 adalah buffer belakang. Fungsi dma_transfer () mengambil tujuan, sumber dan sejumlah 32 bit kata. Kemudian DMA diisi dengan data yang dibutuhkan, dan pekerjaan berlanjut dengan buffer berikutnya.



Setelah mencoba mekanisme ini, kinerja meningkat menjadi sekitar 48 FPS. Sedikit lebih baik dari memcpy (), tetapi hanya sedikit. Saya tidak bermaksud mengatakan bahwa DMA ternyata tidak berguna, tetapi dalam contoh khusus ini, dampak cache pada gambaran besar terlihat lebih baik.



Setelah sedikit terkejut bahwa DMA berkinerja lebih buruk dari yang diharapkan, kami muncul dengan "sangat baik", seperti yang terlihat bagi kami pada saat itu, ide untuk menggunakan beberapa saluran DMA. Apa gunanya? Jumlah data yang dapat dimuat ke DMA sekaligus di stm32f7xx adalah 256 KB. Pada saat yang sama, ingatlah bahwa layar kami berukuran 480x272 dan memori video berukuran sekitar 512 KB, yang berarti Anda dapat meletakkan paruh pertama data ke dalam satu saluran DMA, dan paruh kedua ke yang kedua. Dan semuanya tampak baik-baik saja ... Namun kinerjanya turun dari 48 FPS menjadi 25-30 FPS. Artinya, kami kembali ke situasi ketika cache belum diaktifkan. Dengan apa itu bisa dihubungkan? Faktanya, karena fakta bahwa akses ke memori SDRAM disinkronkan, bahkan memori tersebut disebut Synchronous Dynamic Random Access Memory (SDRAM), jadi opsi ini hanya menambahkan sinkronisasi tambahan,tanpa membuat penulisan ke memori paralel, seperti yang diinginkan. Setelah sedikit refleksi, kami menyadari bahwa tidak ada yang mengejutkan di sini, karena hanya ada satu memori, dan siklus tulis dan baca dihasilkan untuk satu sirkuit mikro (di satu bus), dan karena sumber / penerima lain ditambahkan, maka wasit, yang menyelesaikan panggilan di bus , Anda perlu mencampur siklus perintah dari saluran DMA yang berbeda.



Buffer ganda. Bekerja dengan LTDC



Menyalin dari buffer perantara tentu bagus, tetapi seperti yang kami ketahui, ini tidak cukup. Mari kita lihat peningkatan nyata lainnya - buffering ganda. Di sebagian besar pengontrol tampilan modern, Anda dapat mengatur alamat untuk memori video yang digunakan. Dengan demikian, Anda dapat menghindari penyalinan sama sekali, dan cukup mengatur ulang alamat memori video ke buffer yang telah disiapkan, dan pengontrol layar akan mengambil data secara optimal untuk data itu sendiri melalui DMA. Ini adalah buffering ganda yang nyata, tanpa buffer perantara seperti sebelumnya. Ada juga opsi ketika pengontrol tampilan dapat memiliki dua atau lebih buffer, yang pada dasarnya adalah hal yang sama - kita menulis ke satu buffer, dan yang lain digunakan oleh pengontrol, sementara penyalinan tidak diperlukan.



LTDC (pengontrol tampilan LCD-TFT) di stm32f74xx memiliki dua lapisan overlay perangkat keras - Lapisan 1 dan Lapisan 2, di mana Lapisan 2 ditumpangkan pada Lapisan 1. Setiap lapisan dapat dikonfigurasi secara terpisah dan dapat diaktifkan atau dinonaktifkan secara terpisah. Kami mencoba mengaktifkan hanya Layer 1 dan mengatur ulang alamat memori video di buffer depan atau buffer belakang. Artinya, kami memberikan satu untuk tampilan, dan menggambar yang lain saat ini. Tapi kami mendapat gangguan yang nyata saat mengganti overlay.



Kami mencoba opsi ketika kami menggunakan kedua lapisan dengan salah satu dari mereka hidup / mati, yaitu, ketika setiap lapisan memiliki alamat memori video sendiri, yang tidak berubah, dan buffer diubah dengan menyalakan salah satu lapisan sambil mematikan yang lain. Variasi juga mengakibatkan jitter. Dan akhirnya, kami mencoba opsi ketika lapisan tidak dimatikan, tetapi saluran alfa disetel ke nol 0 atau maksimum (255), yaitu, kami mengontrol transparansi, membuat salah satu lapisan tidak terlihat. Tetapi opsi ini tidak memenuhi harapan, gemetar masih ada.



Alasannya tidak jelas - dokumentasi mengatakan bahwa pembaruan status lapisan dapat dilakukan dengan cepat. Kami membuat tes sederhana - kami mematikan cache, floating point, menggambar gambar statis dengan kotak hijau di tengah layar, sama untuk Layer 1 dan Layer 2, dan mulai beralih level dalam satu lingkaran, berharap mendapatkan gambar statis. Tapi kami mendapat getaran yang sama lagi.



Jelaslah bahwa itu adalah sesuatu yang lain. Dan kemudian kami ingat penyelarasan alamat framebuffer di memori. Karena buffer dialokasikan dari heap dan alamatnya tidak sejajar, kami menyelaraskan alamat mereka sebesar 1 KB - kami mendapatkan gambar yang diharapkan tanpa jitter. Kemudian mereka menemukan dalam dokumentasi bahwa LTDC mengurangi data dalam batch 64 byte, dan bahwa ketidakrataan data memberikan penurunan kinerja yang signifikan. Dalam kasus ini, alamat awal framebuffer dan lebarnya harus sejajar. Untuk mengujinya, kami mengubah lebar 480x4 menjadi 470x4, yang tidak habis dibagi 64 byte, dan mendapat jitter yang sama.



Hasilnya, kami menyelaraskan kedua buffer sebesar 64 byte, memastikan bahwa lebarnya juga sejajar dengan 64 byte dan menjalankan nuklear - jitter menghilang. Solusi yang berhasil terlihat seperti ini. Alih-alih beralih antar lapisan dengan sepenuhnya menonaktifkan baik Lapisan 1 atau Lapisan, gunakan transparansi. Yaitu, untuk menonaktifkan level, setel transparansi ke 0, dan untuk mengaktifkannya - ke 255.



        BSP_LCD_SetTransparency_NoReload(fb_buf_idx, 0xff);

        fb_buf_idx = (fb_buf_idx + 1) % 2;

        BSP_LCD_SetTransparency(fb_buf_idx, 0x00);


Kami mendapat 70-75 FPS! Jauh lebih baik daripada yang asli 15.



Perlu dicatat bahwa solusinya bekerja melalui kontrol transparansi, dan opsi dengan menonaktifkan salah satu level dan opsi dengan mengatur ulang alamat level memberikan gambar jitter pada FPS 40-50 besar, alasannya saat ini tidak kami ketahui. Juga, berjalan ke depan, saya akan mengatakan bahwa ini adalah solusi untuk papan ini.



Adegan perangkat keras diisi melalui DMA2D



Tapi ini bukan batasnya, pengoptimalan terakhir kami untuk meningkatkan FPS adalah pengisian adegan perangkat keras. Sebelumnya kami melakukan pengisian secara terprogram:

nk_rawfb_render(rawfb, nk_rgb(30,30,30), 1);


Sekarang beri tahu plugin rawfb bahwa tidak perlu mengisi adegan, tetapi hanya mengecat:

nk_rawfb_render(rawfb, nk_rgb(30,30,30), 0);


Kami akan mengisi adegan dengan warna yang sama 0xff303030, hanya di perangkat keras melalui pengontrol DMA2D. Salah satu fungsi utama DMA2D adalah menyalin atau mengisi persegi panjang di RAM. Kemudahan utama di sini adalah bahwa ini bukan bagian memori yang berkelanjutan, tetapi area persegi panjang, yang terletak di memori dengan jeda, yang berarti DMA biasa tidak dapat dilakukan dengan segera. Di Embox, kami belum bekerja dengan perangkat ini, jadi mari gunakan alat STM32Cube - fungsi BSP_LCD_Clear (warna uint32_t). Ini memprogram warna isian dan ukuran seluruh layar di DMA2D.



Periode Pengosongan Vertikal (VBLANK)



Tetapi bahkan pada 80 FPS, masalah nyata tetap ada - bagian dari widget bergerak dengan "jeda" kecil saat bergerak melintasi layar. Artinya, widget seolah terbagi menjadi 3 (atau lebih) bagian yang digerakkan berdampingan, namun dengan sedikit penundaan. Ternyata alasannya adalah pembaruan memori video yang salah. Lebih tepatnya, pembaruan pada interval waktu yang salah.



Pengontrol tampilan memiliki properti seperti VBLANK, juga VBI atau Periode Pengosongan Vertikal . Ini menunjukkan interval waktu antara bingkai video yang berdekatan. Atau lebih tepatnya, waktu antara baris terakhir dari bingkai video sebelumnya dan baris pertama dari yang berikutnya. Dalam interval ini, tidak ada data baru yang ditransfer ke tampilan, gambar statis. Untuk alasan ini, aman untuk memperbarui memori video di dalam VBLANK.



Dalam praktiknya, pengontrol LTDC memiliki interupsi yang dikonfigurasi untuk dipicu setelah memproses baris framebuffer berikutnya (daftar konfigurasi posisi interupsi baris LTDC (LTDC_LIPCR)). Jadi, jika kita mengonfigurasi interupsi ini ke nomor baris terakhir, maka kita hanya akan mendapatkan permulaan interval VBLANK. Pada titik ini, kami membuat buffer switching yang diperlukan.



Sebagai hasil dari tindakan tersebut, gambar kembali normal, celahnya hilang. Tetapi pada saat yang sama FPS turun dari 80 menjadi 60. Mari kita pahami apa yang mungkin menjadi alasan perilaku ini. Rumus berikut dapat ditemukan



di dokumentasi :



          LCD_CLK (MHz) = total_screen_size * refresh_rate,


dengan total_screen_size = total_width x total_height. LCD_CLK adalah frekuensi di mana pengontrol tampilan akan memuat piksel dari memori video ke layar (misalnya, melalui Display Serial Interface (DSI)). Tetapi refresh_rate sudah merupakan kecepatan refresh layar itu sendiri, karakteristik fisiknya. Ternyata, mengetahui kecepatan refresh layar dan dimensinya, Anda dapat mengonfigurasi frekuensi untuk pengontrol tampilan. Setelah memeriksa register untuk konfigurasi yang dibuat oleh STM32Cube, kami menemukan bahwa ia menyetel pengontrol ke layar 60 Hz. Jadi semuanya bersatu.



Sedikit tentang perangkat input dalam contoh kami



Mari kita kembali ke aplikasi kita dan melihat bagaimana layar sentuh bekerja, karena seperti yang Anda pahami, antarmuka modern menyiratkan interaktivitas, yaitu interaksi dengan pengguna.



Semuanya diatur cukup sederhana di sini. Peristiwa dari perangkat input diproses dalam loop program utama segera sebelum menampilkan adegan:



        /* Input */
        nk_input_begin(&rawfb->ctx);
        {
            switch (mouse->type) {
            case INPUT_DEV_MOUSE:
                handle_mouse(mouse, fb_info, rawfb);
                break;
            case INPUT_DEV_TOUCHSCREEN:
                handle_touchscreen(mouse, fb_info, rawfb);
                break;
            default:
                /* Unreachable */
                break;
            }
        }
        nk_input_end(&rawfb->ctx);


Penanganan kejadian dari layar sentuh terjadi di fungsi handle_touchscreen ():



handle_touchscreen
static void handle_touchscreen(struct input_dev *ts, struct fb_info *fb_info,
        struct rawfb_context *rawfb) {
    struct input_event ev;
    int type;
    static int x = 0, y = 0;

    while (0 <= input_dev_event(ts, &ev)) {
        type = ev.type & ~TS_EVENT_NEXT;

        switch (type) {
        case TS_TOUCH_1:
            x = normalize_coord((ev.value >> 16) & 0xffff, 0, fb_info->var.xres);
            y = normalize_coord(ev.value & 0xffff, 0, fb_info->var.yres);
            nk_input_button(&rawfb->ctx, NK_BUTTON_LEFT, x, y, 1);
            nk_input_motion(&rawfb->ctx, x, y);
            break;
        case TS_TOUCH_1_RELEASED:
            nk_input_button(&rawfb->ctx, NK_BUTTON_LEFT, x, y, 0);
            break;
        default:
            break;
        }

    }
}




Faktanya, di sinilah peristiwa perangkat input diubah menjadi format yang dipahami Nuklear. Sebenarnya, mungkin itu saja.



Luncurkan di papan lain



Setelah menerima hasil yang lumayan bagus, kami memutuskan untuk memperbanyaknya di papan lain. Kami memiliki papan serupa lainnya - STM32F769I-DISCO. Ada pengontrol LTDC yang sama, tetapi layar berbeda dengan resolusi 800x480. Setelah diluncurkan, mendapat 25 FPS. Artinya, penurunan kinerja yang nyata. Ini mudah dijelaskan dengan ukuran framebuffer - hampir 3 kali lebih besar. Tetapi masalah utama ternyata berbeda - gambar sangat terdistorsi, tidak ada gambar statis pada saat widget harus berada di satu tempat.



Alasannya tidak jelas, jadi kami melihat contoh standar dari STM32Cube. Ada contoh dengan buffering ganda untuk papan khusus ini. Dalam contoh ini, pengembang, tidak seperti metode dengan mengubah transparansi, cukup pindahkan penunjuk ke framebuffer pada interupsi VBLANK. Kami telah mencoba metode ini sebelumnya untuk papan pertama, tetapi tidak berhasil. Tetapi dengan menggunakan metode ini untuk STM32F769I-DISCO, kami mendapat perubahan gambar yang cukup mulus dari 25 FPS.



Senang sekali, kami menguji metode ini lagi (dengan mengatur ulang pointer) pada papan pertama, tetapi masih tidak berhasil pada FPS tinggi. Hasilnya, metode dengan transparansi lapisan (60 FPS) bekerja di satu papan, dan metode dengan penataan ulang penunjuk (25 FPS) di papan lainnya. Setelah mendiskusikan situasinya, kami memutuskan untuk menunda penyatuan sampai studi yang lebih dalam tentang tumpukan grafik.



Hasil



Jadi, mari kita rangkum. Contoh yang ditampilkan mewakili pola GUI sederhana namun umum untuk mikrokontroler - beberapa tombol, kontrol volume, atau yang lainnya. Contoh tersebut tidak memiliki logika apa pun yang terkait dengan peristiwa, karena penekanan ditempatkan pada grafik. Dari segi performa, kami mendapat nilai FPS yang lumayan.



Nuansa yang terakumulasi untuk mengoptimalkan kinerja mengarah pada kesimpulan bahwa grafik menjadi lebih rumit di mikrokontroler modern. Sekarang, seperti pada platform besar, Anda perlu memantau cache prosesor, menempatkan sesuatu di memori eksternal, dan sesuatu di memori yang lebih cepat, menggunakan DMA, menggunakan DMA2D, memantau VBLANK, dan sebagainya. Semuanya mulai terlihat seperti platform besar, dan mungkin itulah sebabnya saya telah merujuk ke X Server dan Wayland beberapa kali.



Mungkin salah satu bagian yang paling tidak dioptimalkan adalah rendering itu sendiri, kami menggambar ulang seluruh adegan dari awal, seluruhnya. Saya tidak dapat mengatakan bagaimana hal itu dilakukan di perpustakaan lain untuk mikrokontroler, mungkin di suatu tempat tahap ini dibangun ke dalam perpustakaan itu sendiri. Tetapi berdasarkan hasil kerja sama dengan Nuklear, tampaknya di tempat ini diperlukan analog X Server atau Wayland, tentu saja, lebih ringan, yang lagi-lagi membawa kita pada gagasan bahwa sistem kecil mengikuti jalur yang besar.



UPD1

Akibatnya, metode dengan mengubah transparansi tidak diperlukan. Di kedua papan, kode umum berfungsi - dengan menukar alamat penyangga dengan v-sync. Selain itu, metode dengan transparansi juga benar, itu tidak perlu.



UPD2

Saya ingin mengucapkan banyak terima kasih kepada semua orang yang menyarankan buffering tiga kali lipat, kami belum mendapatkannya. Tetapi sekarang Anda dapat melihat bahwa ini adalah metode klasik (terutama untuk FPS dengan frekuensi gambar yang tinggi ke tampilan), yang, antara lain, akan memungkinkan kami untuk menghilangkan kelambatan karena menunggu sinkronisasi-v (yaitu ketika perangkat lunak terlihat di depan gambar). Kami belum menemukan ini, tetapi ini hanya masalah waktu. Dan terima kasih khusus untuk diskusi tentang triple buffering yang ingin saya sampaikanbesitzeruf dan belav!



Kontak kami:



Github: https://github.com/embox/embox

Newsletter: embox-ru [at] googlegroups.com

Telegram chat: t.me/embox_chat



All Articles