App:Library:LVGL:docs:Porting:Add custom GPU

提供: robot-jp wiki
ナビゲーションに移動検索に移動

https://docs.lvgl.io/8.2/porting/gpu.html

Add custom GPU

英文 自動翻訳

LVGL has a flexible and extendable draw pipeline.

You can hook it to do some rendering with a GPU or even completely replace the built-in software renderer.

LVGLには、柔軟で拡張可能な描画パイプラインがあります。

これをフックして、GPUでレンダリングを実行したり、組み込みのソフトウェアレンダラーを完全に置き換えたりすることができます。

戻る : Previous

Draw context

英文 自動翻訳

The core structure of drawing is lv_draw_ctx_t.

It contains a pointer to a buffer where drawing should happen and a couple of callbacks to draw rectangles, texts, and other primitives.

描画のコア構造体は lv_draw_ctx_t です。

これには、描画が行われるバッファへのポインタと、長方形、テキスト、およびその他のプリミティブを描画するための2つのコールバックが含まれています。

戻る : Previous

Fields

英文 自動翻訳

lv_draw_ctx_t has the following fields:

  • void * buf Pointer to a buffer to draw into
  • lv_area_t * buf_area The position and size of buf (absolute coordinates)
  • const lv_area_t * clip_area The current clip area with absolute coordinates, always the same or smaller than buf_area. All drawings should be clipped to this area.
  • void (*draw_rect)() Draw a rectangle with shadow, gradient, border, etc.
  • void (*draw_arc)() Draw an arc
  • void (*draw_img_decoded)() Draw an (A)RGB image that is already decoded by LVGL.
  • lv_res_t (*draw_img)() Draw an image before decoding it (it bypasses LVGL's internal image decoders)
  • void (*draw_letter)() Draw a letter
  • void (*draw_line)() Draw a line
  • void (*draw_polygon)() Draw a polygon
  • void (*draw_bg)() Replace the buffer with a rect without decoration like radius or borders.
  • void (*wait_for_finish)() Wait until all background operation are finished. (E.g. GPU operations)
  • void * user_data Custom user data for arbitrary purpose

(For the sake of simplicity the parameters of the callbacks are not shown here.)

All draw_* callbacks receive a pointer to the current draw_ctx as their first parameter.

Among the other parameters there is a descriptor that tells what to draw, e.g. for draw_rect it's called lv_draw_rect_dsc_t, for lv_draw_line it's called lv_draw_line_dsc_t, etc.

To correctly render according to a draw_dsc you need to be familiar with the Boxing model of LVGL and the meanings of the fields.

The name and meaning of the fields are identical to name and meaning of the Style properties.

lv_draw_ctx_t には次のフィールドがあります。
  • void * buf 引き込むバッファへのポインタ
  • lv_area_t * buf_area (絶対座標)bufの位置とサイズ
  • const lv_area_t * clip_area 絶対座標を持つ現在のクリップ領域、常に buf_area と同じか、それより小さくなります。すべての図面はこの領域にクリップする必要があります。
  • void (*draw_rect)() 影,グラデーション,境界線などで長方形を描く。
  • void (*draw_arc)() 円弧を描く。
  • void (*draw_img_decoded)() LVGLによってすでにデコードされている (A)RGB画像 を描画します。
  • lv_res_t (*draw_img)() デコードする前に画像を描画します(LVGLの内部画像デコーダーをバイパスします)
  • void (*draw_letter)() 文字を描く
  • void (*draw_line)() 線を引く
  • void (*draw_polygon)() 折れ線を描く
  • void (*draw_bg)() バッファを、半径や境界線などの装飾のない長方形に置き換えます。
  • void (*wait_for_finish)()すべてのバックグラウンド操作が終了するまで待ちます。(GPU操作など)
  • void * user_data 任意の目的のためのカスタムユーザーデータ

(簡単にするために、コールバックのパラメーターはここには示されていません。)


すべてのdraw_*コールバックは、最初のパラメーターとして現在へのポインターdraw_ctxを受け取ります。

他のパラメータの中には、何を描画するかを指示する記述子があります。たとえば、 draw_rectlv_draw_rect_dsc_t から呼ばれて、lv_draw_linelv_draw_line_dsc_t から呼ばれます。

draw_dsc に従って正しくレンダリングするには、LVGLの枠型モデルとフィールドの意味に精通している必要があります。

フィールドの名前と意味は、Styleプロパティの名前と同じ意味です。

戻る : Previous

Initialization

英文 自動翻訳

The lv_disp_drv_t has 4 fields related to the draw context:

  • lv_draw_ctx_t * draw_ctx Pointer to the draw_ctx of this display
  • void (*draw_ctx_init)(struct _lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx) Callback to initialize a draw_ctx
  • void (*draw_ctx_deinit)(struct _lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx) Callback to de-initialize a draw_ctx
  • size_t draw_ctx_size Size of the draw context structure. E.g. sizeof(lv_draw_sw_ctx_t)

When you ignore these fields, LVGL will set default values for callbacks and size in lv_disp_drv_init() based on the configuration in lv_conf.h.


lv_disp_drv_register() will allocate a draw_ctx based on draw_ctx_size and call draw_ctx_init() on it.

However, you can overwrite the callbacks and the size values before calling lv_disp_drv_register().

It makes it possible to use your own draw_ctx with your own callbacks.

lv_disp_drv_t には、描画コンテキストに関連する4つのフィールドがあります。
  • lv_draw_ctx_t * draw_ctx : このディスプレイの draw_ctx へのポインタ
  • void (*draw_ctx_init)(struct _lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx) : draw_ctx を初期化するためのコールバック
  • void (*draw_ctx_deinit)(struct _lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx) : draw_ctxの初期化を解除するためのコールバック
  • size_t draw_ctx_size : 描画コンテキスト構造のサイズ。例えばsizeof(lv_draw_sw_ctx_t)

これらのフィールドを無視した場合、LVGLは lv_conf.h の configuration に基づき lv_disp_drv_init() にコールバックとサイズのデフォルト値を設定します。


lv_disp_drv_register()draw_ctx_size に基づいて draw_ctx を割り当て、その上で draw_ctx_init() を呼び出します。

ただし、を呼び出す前に、コールバックとサイズの値を上書きできますlv_disp_drv_register()

それはあなた自身のコールバックで、あなた自身である draw_ctx を使うことを可能にします。

戻る : Previous

Software renderer

英文 自動翻訳

LVGL's built in software renderer extends the basic lv_draw_ctx_t structure and sets the draw callbacks. It looks like this:

 typedef struct {
    /** Include the basic draw_ctx type*/
     lv_draw_ctx_t base_draw;
 
     /** Blend a color or image to an area*/
     void (*blend)(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc);
 } lv_draw_sw_ctx_t;

Set the draw callbacks in draw_ctx_init() like:

 draw_sw_ctx->base_draw.draw_rect = lv_draw_sw_rect;
 draw_sw_ctx->base_draw.draw_letter = lv_draw_sw_letter;
 ...


LVGLに組み込まれているソフトウェアレンダラーは、基本の構造体lv_draw_ctx_tを拡張し、描画コールバックを設定します。次のようになります。

 '''typedef struct {
    /**基本的なdraw_ctxタイプを含めます*/
     lv_draw_ctx_t base_draw;
 
     /**色または画像を領域にブレンドします*/
     void* blend)(lv_draw_ctx_t * draw_ctxconst lv_draw_sw_blend_dsc_t * dsc;
 } lv_draw_sw_ctx_t;'''

draw_ctx_init()の中では次のよう に描画コールバックを設定します。

 '''draw_sw_ctx-> base_draw.draw_rect = lv_draw_sw_rect;
 draw_sw_ctx-> base_draw.draw_letter = lv_draw_sw_letter;
 ...'''
戻る : Previous

Blend callback

英文 自動翻訳

As you saw above the software renderer adds the blend callback field. It's a special callback related to how the software renderer works. All draw operations end up in the blend callback which can either fill an area or copy an image to an area by considering an optional mask.

The lv_draw_sw_blend_dsc_t parameter describes what and how to blend. It has the following fields:

  • const lv_area_t * blend_area The area with absolute coordinates to draw on draw_ctx->buf. If src_buf is set, it's the coordinates of the image to blend.
  • const lv_color_t * src_buf Pointer to an image to blend. If set, color is ignored. If not set fill blend_area with color
  • lv_color_t color Fill color. Used only if src_buf == NULL
  • lv_opa_t * mask_buf NULL if ignored, or an alpha mask to apply on blend_area
  • lv_draw_mask_res_t mask_res The result of the previous mask operation. (LV_DRAW_MASK_RES_...)
  • const lv_area_t * mask_area The area of mask_buf with absolute coordinates
  • lv_opa_t opa The overall opacity
  • lv_blend_mode_t blend_mode E.g. LV_BLEND_MODE_ADDITIVE
上で見たように、ソフトウェアレンダラーはblendコールバックフィールドを追加します。これは、ソフトウェアレンダラーの動作に関連する特別なコールバックです。すべての描画操作はblendコールバックで終了します。コールバックは、オプションのマスクを考慮して、領域を塗りつぶすか、画像を領域にコピーすることができます。

この パラメータlv_draw_sw_blend_dsc_tは、何をどのようにブレンドするかを記述します。次のフィールドがあります。

  • const lv_area_t * blend_areadraw_ctx->bufを描画する絶対座標を持つ領域 。src_bufが設定されている場合、ブレンドする画像の座標です。
  • const lv_color_t * src_buf ブレンドする画像へのポインタ。設定されている場合、colorは無視されます。設定されていない場合、colorblend_areaを塗りつぶします。
  • lv_color_t color 塗りつぶしの色。src_buf == NULLの場合にのみ使用されます
  • lv_opa_t * mask_buf 無視された場合はNULL、またはblend_areaに適用するアルファマスク
  • lv_draw_mask_res_t mask_res 前のマスク操作の結果。( LV_DRAW_MASK_RES_...
  • const lv_area_t * mask_areamask_buf 絶対座標のある領域
  • lv_opa_t opa 全体的な不透明度
  • lv_blend_mode_t blend_mode 例えば LV_BLEND_MODE_ADDITIVE
戻る : Previous

Extend the software renderer

New blend callback

英文 自動翻訳

Let's take a practical example: you would like to use your MCUs GPU for color fill operations only.

As all draw callbacks call blend callback to fill an area in the end only the blend callback needs to be overwritten.

First extend lv_draw_sw_ctx_t:

 /*We don't add new fields, so just for clarity add new type*/
 typedef lv_draw_sw_ctx_t my_draw_ctx_t;
 
 void my_draw_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx)
 {
     /*Initialize the parent type first */
     lv_draw_sw_init_ctx(drv, draw_ctx);
 
     /*Change some callbacks*/
     my_draw_ctx_t * my_draw_ctx = (my_draw_ctx_t *)draw_ctx;
 
     my_draw_ctx->blend = my_draw_blend;
     my_draw_ctx->base_draw.wait_for_finish = my_gpu_wait;
 }


After calling lv_disp_draw_init(&drv) you can assign the new draw_ctx_init callback and set draw_ctx_size to overwrite the defaults:

 static lv_disp_drv_t drv;
 lv_disp_draw_init(&drv);
 drv->hor_res = my_hor_res;
 drv->ver_res = my_ver_res;
 drv->flush_cb = my_flush_cb;
 
 /*New draw ctx settings*/
 drv->draw_ctx_init = my_draw_ctx_init;
 drv->draw_ctx_size = sizeof(my_draw_ctx_t);
 
 lv_disp_drv_register(&drv);


This way when LVGL calls blend it will call my_draw_blend and we can do custom GPU operations.

Here is a complete example:

 void my_draw_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc)
 {
     /*Let's get the blend area which is the intersection of the area to fill and the clip area.*/
     lv_area_t blend_area;
     if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return;  /*Fully clipped, nothing to do*/
 
     /*Fill only non masked, fully opaque, normal blended and not too small areas*/
     if(dsc->src_buf == NULL && dsc->mask == NULL && dsc->opa >= LV_OPA_MAX &&
        dsc->blend_mode == LV_BLEND_MODE_NORMAL && lv_area_get_size(&blend_area) > 100) {
 
         /*Got the first pixel on the buffer*/
         lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area); /*Width of the destination buffer*/
         lv_color_t * dest_buf = draw_ctx->buf;
         dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_area.x1 - draw_ctx->buf_area->x1);
 
         /*Make the blend area relative to the buffer*/      
         lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
         
         /*Call your custom gou fill function to fill blend_area, on dest_buf with dsc->color*/  
         my_gpu_fill(dest_buf, dest_stride, &blend_area, dsc->color);
     } 
     /*Fallback: the GPU doesn't support these settings. Call the SW renderer.*/
     else {
       lv_draw_sw_blend_basic(draw_ctx, dsc);
     }
 }


The implementation of wait callback is much simpler:

 void my_gpu_wait(lv_draw_ctx_t * draw_ctx)
 {
     while(my_gpu_is_working());
     
     /*Call SW renderer's wait callback too*/
     lv_draw_sw_wait_for_finish(draw_ctx);
 }

実際の例を見てみましょう。MCUGPUをカラーフィル操作にのみ使用したいとします。


すべての描画コールバックはコールバックblendを呼び出して最終的に領域を埋めるため、blendコールバックのみを上書きする必要があります。

最初の拡張lv_draw_sw_ctx_t

 / *新しいフィールドは追加しないため、わかりやすくするために新しいタイプを追加します* /
 typedef lv_draw_sw_ctx_t my_draw_ctx_t;
 
 void my_draw_ctx_initlv_disp_drv_t * drvlv_draw_ctx_t * draw_ctx
 {
     /*最初に親タイプを初期化します*/
     lv_draw_sw_init_ctxdrvdraw_ctx;
 
     /*一部のコールバックを変更します*/
     my_draw_ctx_t * my_draw_ctx =my_draw_ctx_t *draw_ctx;
 
     my_draw_ctx-> blend = my_draw_blend;
     my_draw_ctx-> base_draw.wait_for_finish = my_gpu_wait;
 }

lv_disp_draw_init(&drv)を呼び出した後、新しいコールバックdraw_ctx_initdraw_ctx_sizeを割り当て、デフォルトを上書きするように 設定できます。

 static lv_disp_drv_t drv;
 lv_disp_draw_init(&drv;
 drv-> hor_res = my_hor_res;
 drv-> ver_res = my_ver_res;
 drv-> flush_cb = my_flush_cb;
 
 /*新しい描画ctx設定*/
 drv-> draw_ctx_init = my_draw_ctx_init;
 drv-> draw_ctx_size = sizeofmy_draw_ctx_t;
 
 lv_disp_drv_register(&drv;


このようにして、LVGLがblend呼び出すと、my_draw_blendが呼び出され、カスタムGPU操作を実行できます。

完全な例を次に示します。

 void my_draw_blendlv_draw_ctx_t * draw_ctxconst lv_draw_sw_blend_dsc_t * dsc
 {
     /*塗りつぶす領域とクリップ領域の交点であるブレンド領域を取得しましょう。*/
     lv_area_t blend_area;
     if(!_ lv_area_intersect(&blend_areadsc-> blend_areadraw_ctx-> clip_area))return; / *完全にクリップされ、何もしません* /
 
     / *マスクされていない、完全に不透明な、通常のブレンドで、小さすぎない領域のみを塗りつぶします* /
     ifdsc-> src_buf == NULL && dsc-> mask == NULL && dsc-> opa> = LV_OPA_MAX &&
        dsc-> blend_mode == LV_BLEND_MODE_NORMAL && lv_area_get_size(&blend_area> 100{
 
         /*バッファの最初のピクセルを取得しました*/
         lv_coord_t dest_stride = lv_area_get_widthdraw_ctx-> buf_area; /*宛先バッファの幅*/
         lv_color_t * dest_buf = draw_ctx-> buf;
         dest_buf + = dest_stride *blend_area.y1-draw_ctx-> buf_area-> y1+blend_area.x1-draw_ctx-> buf_area-> x1;
 
         /*バッファを基準にしてブレンド領域を作成します*/      
         lv_area_move(&blend_area-draw_ctx-> buf_area-> x1-draw_ctx-> buf_area-> y1;
         
         /*カスタムgoufill関数を呼び出して、dest_bufのblend_areaをdsc->colorで塗りつぶします*/  
         my_gpu_filldest_bufdest_stride、&blend_areadsc-> color;
     } 
     / *フォールバック:GPUはこれらの設定をサポートしていませんSWレンダラーを呼び出します。*/
     そうしないと {
       lv_draw_sw_blend_basicdraw_ctxdsc;
     }
 }


待機コールバックの実装ははるかに簡単です。

 void my_gpu_waitlv_draw_ctx_t * draw_ctx
 {
     whilemy_gpu_is_working());
     
     /*SWレンダラーの待機コールバックも呼び出します*/
     lv_draw_sw_wait_for_finishdraw_ctx;
 }
戻る : Previous

New rectangle drawer

英文 自動翻訳

If your MCU has a more powerful GPU that can draw e.g. rounded rectangles you can replace the original software drawer too. A custom draw_rect callback might look like this:

 void my_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords)
 {
   if(lv_draw_mask_is_any(coords) == false && dsc->grad == NULL && dsc->bg_img_src == NULL &&
      dsc->shadow_width == 0 && dsc->blend_mode = LV_BLEND_MODE_NORMAL) 
   {
     /*Draw the background*/
     my_bg_drawer(draw_ctx, coords, dsc->bg_color, dsc->radius);
     
     /*Draw the border if any*/
     if(dsc->border_width) {
       my_border_drawer(draw_ctx, coords, dsc->border_width, dsc->border_color, dsc->border_opa)
     }
     
     /*Draw the outline if any*/
     if(dsc->outline_width) {
       my_outline_drawer(draw_ctx, coords, dsc->outline_width, dsc->outline_color, dsc->outline_opa, dsc->outline_pad)
     }
   } 
   /*Fallback*/
   else {
     lv_draw_sw_rect(draw_ctx, dsc, coords);
   }
 }

my_draw_rect can fully bypass the use of blend callback if needed.

MCUに丸みを帯びた長方形などを描画できるより強力なGPUがある場合は、元のソフトウェアドロワーを置き換えることもできます。カスタムdraw_rectコールバックは次のようになります。
 void my_draw_rectlv_draw_ctx_t * draw_ctxconst lv_draw_rect_dsc_t * dscconst lv_area_t * coords
 {
   iflv_draw_mask_is_anycoords== false && dsc-> grad == NULL && dsc-> bg_img_src == NULL &&
      dsc-> shadow_width == 0 && dsc-> blend_mode = LV_BLEND_MODE_NORMAL
   {
     /*背景を描く*/
     my_bg_drawerdraw_ctxcoordsdsc-> bg_c​​olordsc-> radius;
     
     /*境界線がある場合は描画します*/
     ifdsc-> border_width{
       my_border_drawerdraw_ctxcoordsdsc-> border_widthdsc-> border_colordsc-> border_opa
     }
     
     /*もしあれば輪郭を描きます*/
     ifdsc-> overlay_width{
       my_outline_drawerdraw_ctxcoordsdsc->outline_widthdsc->outline_colordsc->outline_opadsc->outline_pad
     }
   } 
   /*後退する*/
   そうしないと {
     lv_draw_sw_rectdraw_ctxdsccoords;
   }
 }

必要に応じて、my_draw_rectblendblend コールバック の使用を完全にバイパスできます。

戻る : Previous

Fully custom draw engine

英文 自動翻訳

For example if your MCU/MPU supports a powerful vector graphics engine you might use only that instead of LVGL's SW renderer.


In this case, you need to base the renderer on the basic lv_draw_ctx_t (instead of lv_draw_sw_ctx_t) and extend/initialize it as you wish.

たとえば、MCU / MCUが強力なベクターグラフィックエンジンをサポートしている場合は、LVGLのSWレンダラーの代わりにそれだけを使用できます。


この場合、基本に基づいて、(lv_draw_sw_ctx_tではなく)lv_draw_ctx_tレンダラーを必要に応じて拡張/初期化する必要があります。

戻る : Previous