Android HardwareComposerのfenceメカニズム
7619 ワード
最近HardwareComposerと書いていますが、これの主な機能は簡単に言えば、SurfaceFlingerが用意した各layer dataを必要な場所に送り、aospのhardware/samsung_slsi/exynos 5/libhwcというhwcを例にとると、layer dataを/dev/graphics/fb 0に送って表示する.
しかし、いわゆる「sf準備のlayer」は、実際には必ずしも準備ができているとは限らない.
例えば、grallocが割り当てる1枚のbufferはapkとsfの間で共有され、apkはbufferのproducer、sfはbufferのconsumerとする.apkは必要なgl描画操作を実行すると、bufferの制御権をsfに渡し、sfはbufferに対応するhandleをhwcに渡してcomposeをします.このときapkのglのfuncはすでにreturnされていますが、本当の操作はgpuのqueueの中にあるかもしれません.bufferの内容はまだ更新されていません.hwcがbuffer情報をfb driverに送ると、fb driverがすぐにbufferの内容を画面に表示すると、エラーの画面が見える.逆に、画面の内容が表示されている状態で、apkが同じbuffer上に再描画する場合も同様の問題が発生する.したがって、producerとconsumerの間の同期メカニズム、すなわち本編で議論するfenceが必要である.
fenceがhwcでどのように運用されているかを見てみましょう.sfにhwcを表示する内容がある場合、sfはcall hwcのset関数(実際にはその前にcall prepareもありますが、prepareはfenceを処理しません).set関数returnの前にlayerの本当の準備を待つ必要はありません.fenceを正しく処理すれば、bufferはreadyの時にsignal関連のfenceをして、本当にbuffer dataを消費する人に知らせます.
hardware/libhardware/include/hardware/hwcomposer.h
numHwLayersは今回表示する必要があるlayer個数を表し、hwLyaers[0]はサイズ0の配列でnumHwLayersの次のアドレスを指し、sfはhwc_を構築している.display_contents_1_tの場合numHwLayersによってsizeof(hwc_display_contents_1_t)+numHwLayers*sizeof(hwc_layer_1_t)の大きさのmemoryが割り当てられ、hwLyaersはnumHwLayers個のhwc_を本当に保存したことを指すlayer_1_tのアドレス
retireFenceFdは今回のcomposition retire後にsingalされる必要があり、physical displayにとってretireの概念は、今回の画面を次の画面に置き換えるときである.virtual displayにとってretireの概念は、コンテンツがoutbufferに完全に書き込まれ、readできる場合である.総じて言えば今回のcompositionの結果が必要とされなくなったとき、hwc_display_contents_1_tはretireとマークされています.
retireFenceFdはcomposition全体、つまりグループ全体のlayerを制御する.
またhwc_を見てみましょうlayer_1_t
acquireFenceFdとreleaseFenceFdは単一layerを制御する.
まだhardware/samsung_slsi/exynos 5/libhwc/を例に挙げると、(簡略化codeはfenceに関連する部分のみを保持し、完全なcodeはaospを参照してください):
各layerは1つのconfigに対応し、すべてのconfigはwinを構成する.data,exynos5_config_overlayは各layerからconfigの必要な情報を抽出してcofnigに記入し、最後にwin全体を記入します.dataをパラメータとしてS 3 CFB_WIN_CONFIG ioctlはfb 0に送りました、exynos 5_post_fimd returnのfenceは実はfb driver処理後のwin_data.fence.
acqireFenceFdはまだ現れていませんが、ioctlの前に各configのfence_fdは-1,ioctlに設定された後、再びfence_を判断するfd-1でなければcloseですconfig.fence_fdはacqireFenceFdと関係があるに違いない!でもここでclose fence_fdは少し早すぎるのではないでしょうか.このfenceはもう必要ありませんか?set関数が戻るまでacquireFenceFdがsignalされるのを待つ必要はないのではないでしょうか.さらにexynos 5に入りますconfig_見てみろ
これでhwcにおけるfenceに対する処理フローは終了し,以下をまとめる.
1. hwcはすべてのlayer->acquireFenceFdをfb driverに一緒に送った後、layer->acquireFenceFd closeをfb driverに渡し、fenceの管理権をfb driverに渡す.
2. ioctl rentrunの場合、win_data.fenceはdriverによって新しいfdとして記入され、このfdはdupによってn回与えられ、各layerのlayer->releaseFenceFdに与えられる.
3. win_data.fenceはhwc全体に値を付与するdisplay_contents_1_t、つまりすべてのlayerの組み合わせのretireFenceFd.
4.その後releaseFenceFdとretaireFenceFdの生死はsfに任せた.
次の議論はfd driverに入ります.Android fb driverのfenceメカニズムを参照してください.
しかし、いわゆる「sf準備のlayer」は、実際には必ずしも準備ができているとは限らない.
例えば、grallocが割り当てる1枚のbufferはapkとsfの間で共有され、apkはbufferのproducer、sfはbufferのconsumerとする.apkは必要なgl描画操作を実行すると、bufferの制御権をsfに渡し、sfはbufferに対応するhandleをhwcに渡してcomposeをします.このときapkのglのfuncはすでにreturnされていますが、本当の操作はgpuのqueueの中にあるかもしれません.bufferの内容はまだ更新されていません.hwcがbuffer情報をfb driverに送ると、fb driverがすぐにbufferの内容を画面に表示すると、エラーの画面が見える.逆に、画面の内容が表示されている状態で、apkが同じbuffer上に再描画する場合も同様の問題が発生する.したがって、producerとconsumerの間の同期メカニズム、すなわち本編で議論するfenceが必要である.
fenceがhwcでどのように運用されているかを見てみましょう.sfにhwcを表示する内容がある場合、sfはcall hwcのset関数(実際にはその前にcall prepareもありますが、prepareはfenceを処理しません).set関数returnの前にlayerの本当の準備を待つ必要はありません.fenceを正しく処理すれば、bufferはreadyの時にsignal関連のfenceをして、本当にbuffer dataを消費する人に知らせます.
hardware/libhardware/include/hardware/hwcomposer.h
int (*set)(struct hwc_composer_device_1 *dev,
size_t numDisplays, hwc_display_contents_1_t** displays);
hwc_display_contents_1_tは、fenceに関係のない部分を無視するように定義されています.typedef struct hwc_display_contents_1 {
int retireFenceFd;
size_t numHwLayers;
hwc_layer_1_t hwLayers[0];
} hwc_display_contents_1_t;
numHwLayersは今回表示する必要があるlayer個数を表し、hwLyaers[0]はサイズ0の配列でnumHwLayersの次のアドレスを指し、sfはhwc_を構築している.display_contents_1_tの場合numHwLayersによってsizeof(hwc_display_contents_1_t)+numHwLayers*sizeof(hwc_layer_1_t)の大きさのmemoryが割り当てられ、hwLyaersはnumHwLayers個のhwc_を本当に保存したことを指すlayer_1_tのアドレス
retireFenceFdは今回のcomposition retire後にsingalされる必要があり、physical displayにとってretireの概念は、今回の画面を次の画面に置き換えるときである.virtual displayにとってretireの概念は、コンテンツがoutbufferに完全に書き込まれ、readできる場合である.総じて言えば今回のcompositionの結果が必要とされなくなったとき、hwc_display_contents_1_tはretireとマークされています.
retireFenceFdはcomposition全体、つまりグループ全体のlayerを制御する.
またhwc_を見てみましょうlayer_1_t
typedef struct hwc_layer_1 {
...
buffer_handle_t handle;
int acquireFenceFd;
int releaseFenceFd;
...
} hwc_layer_1_t;
handleは本layerに対応するbufferである.set関数がcallに到着するとbufferが本当にreadyかどうか分からないが、acquireFenceFdはbufferの内容が本当にreadyの時にsignalされ、hwcはclose acquireFenceFdを担当する.set関数returnの前にbufferの内容が使用済みであることを保証することはできないので、hwcはこのbufferのためにfenceを構築する必要があります.releaseFenceFdはset関数returnの前にsetされ、sfはreleaseFenceFdがsignalされるのを待ってからbufferを再利用します.sfはclose release FenceFdを担当します.acquireFenceFdとreleaseFenceFdは単一layerを制御する.
まだhardware/samsung_slsi/exynos 5/libhwc/を例に挙げると、(簡略化codeはfenceに関連する部分のみを保持し、完全なcodeはaospを参照してください):
static int exynos5_set(struct hwc_composer_device_1 *dev,
size_t numDisplays, hwc_display_contents_1_t** displays)
{
if (!numDisplays || !displays)
return 0;
exynos5_hwc_composer_device_1_t *pdev =
(exynos5_hwc_composer_device_1_t *)dev;
hwc_display_contents_1_t *fimd_contents = displays[HWC_DISPLAY_PRIMARY];
int fimd_err = 0;
if (fimd_contents)
fimd_err = exynos5_set_fimd(pdev, fimd_contents);
return fimd_err;
}
set関数取得番号HWC_DISPLAY_PRIMARYのhwc_display_contents_1_t,すなわちandorid内蔵メインディスプレイに表示する内容,callからexynos 5_set_fimd. static int exynos5_set_fimd(exynos5_hwc_composer_device_1_t *pdev,
hwc_display_contents_1_t* contents)
{
hwc_layer_1_t *fb_layer = NULL;
int err = 0;
int fence;
if (!err) {
fence = exynos5_post_fimd(pdev, contents);
if (fence < 0)
err = fence;
}
for (size_t i = 0; i < NUM_HW_WINDOWS; i++) {
int dup_fd = dup(fence);
layer.releaseFenceFd = dup_fd;
}
contents->retireFenceFd = fence;
return err;
}
exynos5_post_fimd returnはint fence、fenceは実はfdで、hwcはこのfdをn回dupして、layerのreleaseFeceFdごとに値を与えて、最後にfence自身をlayerグループ全体のretireFenceFdに値を与えます.すなわち、retireFenceFdとすべてのreleaseFenceFdはkernelに対応する同じfileである.これまでacqireFenceFdは出場しておらず、まだexynos 5_にいるに違いない.post_fimd中dev->fd = open("/dev/graphics/fb0", O_RDWR);
static int exynos5_post_fimd(exynos5_hwc_composer_device_1_t *pdev,
hwc_display_contents_1_t* contents)
{
exynos5_hwc_post_data_t *pdata = &pdev->bufs;
struct s3c_fb_win_config_data win_data;
struct s3c_fb_win_config *config = win_data.config;
memset(config, 0, sizeof(win_data.config));
for (size_t i = 0; i < NUM_HW_WINDOWS; i++)
config[i].fence_fd = -1;
for (size_t i = 0; i < NUM_HW_WINDOWS; i++) {
int layer_idx = pdata->overlay_map[i];
if (layer_idx != -1) {
hwc_layer_1_t &layer = contents->hwLayers[layer_idx];
private_handle_t *handle =
private_handle_t::dynamicCast(layer.handle);
exynos5_config_overlay(&layer, config[i], pdev);
}
}
int ret = ioctl(pdev->fd, S3CFB_WIN_CONFIG, &win_data);
for (size_t i = 0; i < NUM_HW_WINDOWS; i++)
if (config[i].fence_fd != -1)
close(config[i].fence_fd);
return win_data.fence;
}
各layerは1つのconfigに対応し、すべてのconfigはwinを構成する.data,exynos5_config_overlayは各layerからconfigの必要な情報を抽出してcofnigに記入し、最後にwin全体を記入します.dataをパラメータとしてS 3 CFB_WIN_CONFIG ioctlはfb 0に送りました、exynos 5_post_fimd returnのfenceは実はfb driver処理後のwin_data.fence.
acqireFenceFdはまだ現れていませんが、ioctlの前に各configのfence_fdは-1,ioctlに設定された後、再びfence_を判断するfd-1でなければcloseですconfig.fence_fdはacqireFenceFdと関係があるに違いない!でもここでclose fence_fdは少し早すぎるのではないでしょうか.このfenceはもう必要ありませんか?set関数が戻るまでacquireFenceFdがsignalされるのを待つ必要はないのではないでしょうか.さらにexynos 5に入りますconfig_見てみろ
static void exynos5_config_overlay(hwc_layer_1_t *layer, s3c_fb_win_config &cfg,
exynos5_hwc_composer_device_1_t *pdev)
{
private_handle_t *handle = private_handle_t::dynamicCast(layer->handle);
exynos5_config_handle(handle, layer->sourceCrop, layer->displayFrame,
layer->blending, layer->acquireFenceFd, cfg, pdev);
}
static void exynos5_config_handle(private_handle_t *handle,
hwc_rect_t &sourceCrop, hwc_rect_t &displayFrame,
int32_t blending, int fence_fd, s3c_fb_win_config &cfg,
exynos5_hwc_composer_device_1_t *pdev)
{
uint32_t x, y;
uint32_t w = WIDTH(displayFrame);
uint32_t h = HEIGHT(displayFrame);
uint8_t bpp = exynos5_format_to_bpp(handle->format);
uint32_t offset = (sourceCrop.top * handle->stride + sourceCrop.left) * bpp / 8;
cfg.state = cfg.S3C_FB_WIN_STATE_BUFFER;
cfg.fd = handle->fd;
cfg.x = x;
cfg.y = y;
cfg.w = w;
cfg.h = h;
cfg.format = exynos5_format_to_s3c_format(handle->format);
cfg.offset = offset;
cfg.stride = handle->stride * bpp / 8;
cfg.blending = exynos5_blending_to_s3c_blending(blending);
cfg.fence_fd = fence_fd;
}
acquireFenceFdは、layer毎に対応するconfigのcfgとしてついに現れる.fence_fd、S 3 CFB_経由WIN_CONFIG ioctrlはfb driverに送った.fb driverを送った以上、fenceの生死はdriverが責任を負う.hwcはもう気にしないでください.だから上のexynos 5_post_各layerのconfigをfence_fd、つまりlayer->acquireFenceFdはcloseに落ちた.これでhwcにおけるfenceに対する処理フローは終了し,以下をまとめる.
1. hwcはすべてのlayer->acquireFenceFdをfb driverに一緒に送った後、layer->acquireFenceFd closeをfb driverに渡し、fenceの管理権をfb driverに渡す.
2. ioctl rentrunの場合、win_data.fenceはdriverによって新しいfdとして記入され、このfdはdupによってn回与えられ、各layerのlayer->releaseFenceFdに与えられる.
3. win_data.fenceはhwc全体に値を付与するdisplay_contents_1_t、つまりすべてのlayerの組み合わせのretireFenceFd.
4.その後releaseFenceFdとretaireFenceFdの生死はsfに任せた.
次の議論はfd driverに入ります.Android fb driverのfenceメカニズムを参照してください.