خط لوله گرافیکی HAR

این صفحه جزئیات کامل خط لوله گرافیکی رندرکننده با قابلیت دسترسی بالا (HAR) را شرح می‌دهد و جریان داده‌ها را از یک سند طراحی فیگما تا پیکسل‌های نهایی نمایش داده شده روی صفحه نمایش می‌دهد.

نمای کلی

این خط لوله، تعاریف رابط کاربری سطح بالا را به دستورات گرافیکی سطح پایین تبدیل کرده و آنها را به طور کارآمد روی نمایشگرهای سخت‌افزاری ارائه می‌دهد. این خط لوله برای برنامه‌های کاربردی حیاتی برای ایمنی خودرو طراحی شده است و بر رندر قطعی، مدیریت کارآمد وضعیت و تعامل قوی با زیرسیستم‌های گرافیکی پلتفرم، مانند مدیر رندر مستقیم (DRM) و مدیریت بافر عمومی (GBM) تأکید دارد.

خط لوله را می‌توان به چهار مرحله اصلی تقسیم کرد:

  1. پیش‌رندر: پردازش نمودار صحنه، اعمال سفارشی‌سازی‌ها و حل طرح‌بندی.
  2. تولید فرمان: تبدیل نمودار صحنه‌ی حل‌شده به یک لیست نمایش بدون وابستگی به بک‌اند.
  3. رندرینگ: اجرای دستورات ترسیم با استفاده از موتور گرافیکی ایمپرلِر.
  4. ارائه: مدیریت فریم‌بافرها و همگام‌سازی با سخت‌افزار نمایشگر.

جریان گرافیکی HAR

شکل ۱. جریان گرافیکی HAR.

مرحله ۱: پیش‌رندر

این مرحله، طراحی استاتیک فیگما و حالت پویای برنامه را به یک درخت رابط کاربری کاملاً حل‌شده و درون حافظه‌ای آماده برای رندر تبدیل می‌کند. این مرحله روی یک نخ کاهنده اختصاصی، جدا از حلقه نمایش اصلی، اجرا می‌شود.

۱.۱ بنیاد DesignCompose

خط لوله HAR بر روی اکوسیستم DesignCompose ساخته شده است.

  • منبع: رابط کاربری در فیگما طراحی شده و با استفاده از افزونه DesignCompose خروجی گرفته شده است.
  • تعریف: خروجی یک نمونه از DesignComposeDefinition است، یک نمایش سریالی از طراحی (گره‌ها، سبک‌ها، انواع).
  • اتصال داده: مدل رابط کاربری برنامه از ماکروهای رویه‌ای (برای مثال، #[Design(node = "#speed")] ) برای اتصال صریح فیلدهای Rust struct به گره‌های نامگذاری شده خاص در سند Figma استفاده می‌کند. این امر به حالت برنامه اجازه می‌دهد تا به طور خودکار ویژگی‌های عناصر بصری را هدایت کند.

اجزای اصلی این بنیاد عبارتند از:

  • Reducer: به عنوان حلقه رویداد مرکزی عمل می‌کند، اقدامات را پردازش کرده و وضعیت فعلی را به‌روزرسانی می‌کند. این چارچوب DefaultReducer ارائه می‌دهد، اما در صورت نیاز می‌توان پیاده‌سازی سفارشی reducer را نیز ارائه داد.
  • ارائه‌دهنده: وضعیت فعلی را به مدل رابط کاربری متصل می‌کند. ویژگی Presenter توسط جعبه چارچوب harry مشخص شده است و یک پیاده‌سازی مرجع ( UIModelPresenter ) در جعبه harry-app-core ارائه شده است.
  • مدل رابط کاربری: سفارشی‌سازی‌ها را بر اساس وضعیت فعلی تولید می‌کند. کد مدل رابط کاربری با استفاده از ماکروی DesignDocument که توسط کادر derive_customizations ارائه می‌شود، تولید می‌شود. struct UIModel در کادر harry-app-core نمونه‌ای از این مورد را ارائه می‌دهد.
  • Squoosh: ساختار داده SquooshView و مخزن گونه‌ها را فراهم می‌کند که برای رندر رابط کاربری مطابق با طراحی استفاده می‌شود. یک سند طراحی سریالی شده توسط جعبه dc_bundle از کتابخانه DesignCompose بارگذاری شده و برای عملکرد کارآمد در زمان اجرا به درختی از ساختارهای SquooshView تبدیل می‌شود.

۱.۲ حلقه کاهنده

این خط لوله توسط اکشن‌ها هدایت می‌شود. این چارچوب نوع شمارش‌شده‌ی Actions را مشخص می‌کند که اکشن‌های داخلی مورد استفاده توسط خود چارچوب را تعریف می‌کند، اما همچنین شامل یک نوع CustomAction است که به کاربران امکان می‌دهد اکشن‌های خاص برنامه‌ی اضافی (مثلاً UpdateVehicleSpeed ​​یا ButtonPress ) را تعریف کنند.

این چارچوب همچنین ویژگی StateAction را ارائه می‌دهد که پیاده‌سازی اقداماتی را که بر وضعیت برنامه تأثیر می‌گذارند و به صورت اختیاری عوارض جانبی ایجاد می‌کنند، ساده می‌کند و سپس از reducer برای پردازش به برنامه بازگردانده می‌شوند. شمارش CustomActions در جعبه harry-app-core مثالی دقیق از این مورد ارائه می‌دهد.

این یک طرح کلی از حلقه کاهنده است:

  • پردازش عمل: Reducer یک عمل را دریافت می‌کند و وضعیت فعلی را به‌روزرسانی می‌کند. این داده‌های خام مانند سرعت فعلی یا اینکه کدام چراغ‌های هشدار دهنده فعال هستند، هستند. این ممکن است عوارض جانبی نیز ایجاد کند (برای مثال، وقتی چراغ کمربند ایمنی چشمک می‌زند، سیگنالی پخش شود).
  • ارائه: Presenter حالت جدید را به UIModel نگاشت می‌کند. UIModel یک مدل نمایش است که داده‌هایی را که به‌طور خاص برای رابط کاربری قالب‌بندی شده‌اند، نگهداری می‌کند (برای مثال، قالب‌بندی سرعت "120" به رشته "65 مایل در ساعت").
  • تولید سفارشی‌سازی: متد apply مدل رابط کاربری برای تولید مجموعه‌ای از نمونه‌های RenderCustomization فراخوانی می‌شود. اینها دستورالعمل‌های صریحی برای اصلاح طراحی فیگما هستند (برای مثال، "متن گره #سرعت را روی '65 مایل در ساعت' تنظیم کن").
  • UpdatePolicy برای بهینه‌سازی: پس از هر بار پیش رندر، یک مقدار UpdatePolicy برگردانده می‌شود که نشان می‌دهد چه زمانی به‌روزرسانی رندر بعدی مورد نیاز است. اگر هیچ تغییر وضعیتی در انتظار نباشد و هیچ انیمیشنی در حال اجرا نباشد، UpdatePolicy سیگنال می‌دهد که هیچ به‌روزرسانی بیشتری فوراً مورد نیاز نیست. در چنین مواردی، Reducer تولید لیست‌های نمایش جدید را متوقف می‌کند، از چرخه‌های رندر غیرضروری جلوگیری می‌کند و منابع را تا زمانی که یک اقدام یا رویداد جدید باعث ایجاد تغییر شود، حفظ می‌کند.

۱.۳ مشاهده‌ی مصرف و مقداردهی اولیه‌ی مخزن

این خط لوله با یک نمونه DesignComposeDefinition آغاز می‌شود. این سند طراحی Figma است که توسط DesignCompose در یک ساختار بافر پروتکل سریالی شده است.

  • بارگذاری اولیه: در هنگام راه‌اندازی، طرح اصلی (که توسط گره ریشه آن مشخص می‌شود) از DesignComposeDefinition به یک درخت SquooshView اولیه تبدیل می‌شود. این فرآیند یک‌باره است.

  • مخزن: SquooshVariantRepository انواع کامپوننت‌های قابل استفاده مجدد و نماهای بارگذاری شده اولیه را مدیریت می‌کند.

  • بارگذاری تنبل: برای به حداقل رساندن زمان راه‌اندازی و استفاده از حافظه، نماهای اضافی (آن‌هایی که بخشی از درخت گره ریشه اولیه نیستند) فقط زمانی که صریحاً به آن‌ها ارجاع داده شده و منطق رندر به آن‌ها نیاز داشته باشد (مثلاً در طول سفارشی‌سازی لیست)، به صورت تنبل از سند بارگذاری می‌شوند.

۱.۴ مرحله شخصی‌سازی

درخت SquooshView پیمایش می‌شود تا حالت پویای برنامه اعمال شود:

  • تعویض‌های متغیر: نمونه‌های کامپوننت با متغیرهای خاص (مثلاً تغییر آیکونی که نشان‌دهنده حالت رانندگی فعلی از اسپرت به اقتصادی است) بر اساس منطق زمان اجرا تعویض می‌شوند.

  • گسترش لیست: یک آیتم قالب واحد در فیگما با یک لیست پویا از فرزندان جایگزین می‌شود. شناسه‌های منحصر به فرد جدیدی برای این فرزندان ایجاد می‌شود تا هویت پایدار انیمیشن‌ها تأیید شود.

  • لغو متن و سبک: محتوای متن (برای مثال، مقدار سرعت) و سبک‌ها (برای مثال، شفافیت، رنگ) از حالت فعلی به‌روزرسانی می‌شوند.

۱.۵ وضوح متغیر

توکن‌ها و متغیرهای طراحی که در فیگما یا به صورت محلی در برنامه تعریف شده‌اند، قابل حل هستند.

  • اتصال: ویژگی‌های SquooshView که به متغیرهایی (مانند رنگ‌ها یا ابعاد) ارجاع می‌دهند، با مقادیر واقعی آنها برای فریم فعلی جایگزین می‌شوند.

۱.۶ محاسبه طرح‌بندی

  • طرح‌بندی پویا: DynamicLayout موقعیت و اندازه نهایی (مرزهای) هر گره در درخت SquooshView را محاسبه می‌کند.

  • طرح‌بندی متن: TextHelper از پیاده‌سازی ویژگی LayoutHelper برای محاسبه معیارهای متن، پوشش و شکل‌دهی استفاده می‌کند. این امر به تأیید صحت جریان متن در محدودیت‌های آن قبل از رندر شدن کمک می‌کند.

۱.۷ صفحه مدرج و گیج‌ها

این یک مرحله تخصصی برای رابط‌های کاربری خودرو است.

  • MeterData : اگر یک گره دارای داده‌های کنتور (تعریف‌شده در فیگما) باشد، هندسه آن به صورت پویا بر اساس meter_value (مثلاً سرعت خودرو) تغییر می‌کند.
    • کمان‌ها: زاویه جاروب تنظیم می‌شود.
    • چرخش‌ها: تبدیل چرخش بر اساس زوایای شروع و پایان محاسبه می‌شود.
    • نوارهای پیشرفت: عرض یا ارتفاع یک مستطیل مقیاس‌بندی می‌شود.
    • بردارهای پیشرفت: طول یک مسیر برداری تنظیم می‌شود.

۱.۸ انیمیشن

  • تفاوت: SquooshView فعلی با previous_squoosh_view از PreRenderCache مقایسه می‌شود.

  • درون‌یابی: اگر ویژگی‌ها تغییر کرده باشند، Squoosh درون‌یابی‌هایی ایجاد می‌کند تا مقادیر (مثلاً شفافیت یا تبدیل) را به مرور زمان به طور روان تغییر دهد.

مرحله ۲: تولید فرمان

پس از اینکه درخت SquooshView به طور کامل حل و متحرک‌سازی شد، به یک توالی خطی از دستورات ترسیم تبدیل می‌شود.

جزء کلیدی این مرحله، جعبه DisplayList است:

  • generate_dl : این تابع به صورت بازگشتی درخت SquooshView را پیمایش می‌کند.

  • ترجمه:

    • شکل‌ها و مسیرها: با نوع مناسب DisplayListAppearance (مثلاً Rect یا Path ) به DisplayListEntry تبدیل می‌شوند.
    • متن: با TextHelper به ورودی‌های طراحی متن تبدیل شده است.
    • تبدیل‌ها و کلیپ‌ها: برای مدیریت پشته حالت ترسیم، به جفت‌های PushTransform3D و PopTransform3D یا PushClipRegion و PopClipRegion تبدیل می‌شوند.
    • ماسک کردن: برای ایجاد و ترکیب صحیح لایه‌ها، به جفت‌های PushMaskLayer و PopMaskLayer تبدیل شده است.

نتیجه نهایی یک نمونه از Vec<DisplayListEntry> است که مستقل از نحوه ترسیم، آنچه را که باید ترسیم شود، توصیف می‌کند.

۲.۱ انتقال به حلقه‌زن

پس از تولید DisplayList ، Reducer آن را در نمونه‌ای از ViewDescriptor قرار می‌دهد و آن را از طریق کانال Rust MPSC ( LooperMessage ) به نخ looper ارسال می‌کند. Looper مسئول مراحل رندر و نمایش است که مانع از مسدود شدن خط لوله گرافیکی توسط نخ Reducer می‌شود.

مرحله ۳: رندرینگ

DisplayList مستقل از پلتفرم به بخش رندرینگ تحویل داده می‌شود، جایی که دستورات انتزاعی به دستورالعمل‌های GPU ترجمه می‌شوند.

HAR از Impeller، یک موتور رندر که در ابتدا برای Flutter ساخته شده بود، استفاده می‌کند. Impeller برای حل مشکل اشکالات نرخ فریم ناشی از کامپایل شیدرها طراحی شده است که این کار را با پیش‌کامپایل کردن مجموعه‌ای کوچک و کارآمد از شیدرها در زمان ساخت انجام می‌دهد. این رویکرد، همراه با دسته‌بندی مؤثر و یک backend بسیار بهینه، موارد زیر را ارائه می‌دهد:

  • عملکرد قطعی: عملاً اشکالات کامپایل سایه‌زن در زمان اجرا را از بین می‌برد.
  • راه‌اندازی سریع: سربار راه‌اندازی اولیه را کاهش می‌دهد.
  • ردپای کوچک: اندازه دودویی فشرده‌ای تولید می‌کند.

برای آشنایی کامل با معماری Impeller، [معرفی Impeller - موتور رندر جدید Flutter] [impeller-video] را تماشا کنید. اگرچه این ویدیو در مورد Flutter صحبت می‌کند، اما این مزایای اصلی مستقیماً به پشته خودروسازی HAR قدرت می‌دهند.

اجزای کلیدی مرحله رندرینگ عبارتند از:

  • ImpellerRenderer : لیست نمایش را از مرحله پیش رندر به دستورات رندر Impeller تبدیل می‌کند.

  • رابط برنامه‌نویسی کاربردی Impeller Rust: کتابخانه Impeller را برای استفاده در Rust (جعبه‌های impeller و impeller-rs-bindgen ) در بر می‌گیرد.

  • TypographyContext : ثبت فونت و شکل‌دهی متن را مدیریت می‌کند.

پروانه-ویدئو

۳.۱ مقداردهی اولیه و مدیریت سطح

  • ایجاد زمینه: رندرکننده یک نمونه از impeller::Context با یک backend از OpenGL ES مقداردهی اولیه می‌کند و یک callback برای حل اشاره‌گرهای تابع OpenGL ES از زمینه GL پلتفرم ارسال می‌کند.

  • سطح FBO پیچیده شده: به جای ایجاد پنجره‌ی خود، Impeller در یک شیء فریم‌بافر OpenGL (FBO) موجود که توسط فاز ۴ ارائه شده است، رندر می‌شود. این کار با فراخوانی Surface::create_wrapped_fbo انجام می‌شود.

۳.۲ مدیریت منابع

  • تصاویر: از فرمت‌های استاندارد و بافت‌های فشرده KTX2 پشتیبانی می‌کند. این تصاویر در بافت‌های GPU آپلود شده و توسط یک ساختار Resources داخلی مدیریت می‌شوند.

  • فونت‌ها: فونت‌های TrueType و OpenType برای رندر متن، در TypographyContext بارگذاری و ثبت می‌شوند.

  • تصاویر خارجی: مدیریت تخصصی بافت‌های خارجی (برای مثال، فیدهای دوربین و رندرکننده‌های سه‌بعدی خارجی) شامل اتصال نمونه‌های EGLImage یا بافت‌های OpenGL خارجی به اشیاء Impeller Texture برای رندر بدون کپی است.

۳.۳ رندر پس

حلقه render با استفاده از DisplayListBuilder یک نمونه از Impeller DisplayList می‌سازد (نباید با Vec<DisplayListEntry> تولید شده توسط مرحله پیش رندر اشتباه گرفته شود):

  1. بافر را پاک می‌کند و تبدیل‌های سراسری را برای مقیاس‌بندی DPI و چرخش صفحه نمایش اعمال می‌کند.

  2. روی آیتم‌های ورودی DisplayListEntry تکرار می‌کند:

    • حالت: save() و restore() برای ارسال و حذف تبدیل‌ها و برش ناحیه‌ها استفاده می‌شود.
    • اشکال هندسی اولیه: Rect و RoundedRect با استفاده از عملیات استاندارد رنگ‌آمیزی ترسیم می‌شوند.
    • مسیرها: مسیرهای برداری پیچیده (از جمله نمونه‌های Arc پویا) ساخته و ترسیم می‌شوند.
    • متن: Text و StyledText با استفاده از TypographyContext رندر می‌شوند.
    • تصاویر: تصاویر استاندارد و خارجی با استفاده از draw_texture_rect ترسیم می‌شوند.
  3. لیست نمایش پروانه ساخته شده را با استفاده از surface.draw_display_list() به سطح ارسال می‌کند و دستورات GL زیرین را تولید می‌کند.

  4. تابع swap_buffers() را روی context اصلی فراخوانی می‌کند تا فاز ۴ را آغاز کند.

مرحله ۴: ارائه

این مرحله نهایی، تعامل با سخت‌افزار نمایشگر را برای نمایش فریم رندر شده مدیریت می‌کند. HAR از یک مسیر رندر مستقیم قوی در سیستم عامل اندروید اتوموتیو (AAOS) و سیستم عامل نرم‌افزاری تعریف‌شده برای خودرو (SDV) استفاده می‌کند.

مولفه کلیدی این مرحله HarDirectRenderingContext (در جعبه har-gl-context ) است.

۴.۱ معماری

لایه ارائه از یک رویکرد بافر دوگانه با یک هدف ترسیم خارج از صفحه استفاده می‌کند:

  1. بافر ترسیم: FBO خارج از صفحه، جایی که ایمپلر صحنه را رندر می‌کند.

  2. بافر Resolve (اختیاری): بافر کمکی اختیاری برای پشتیبانی از ضد الاسینگ چند نمونه‌ای (MSAA)

    • این می‌تواند در صورت نیاز توسط پیاده‌سازی یا پیکربندی اساسی OpenGL ES فعال شود. در چنین مواردی، به عنوان یک هدف میانی برای حل بافر ترسیم چند نمونه‌ای قبل از blitting (انتقال بلوک بیت) به بافر رندر عمل می‌کند.
  3. بافر رندر: بافر عمومی که توسط یک شیء GBM پشتیبانی می‌شود، که معادل بافر پشتی در یک زنجیره‌ی مبادله‌ی گرافیکی معمولی است.

  4. بافر جلویی: بافر GBM که به صفحه نمایش اسکن می‌شود.

۴.۲ زنجیره سواپ

وقتی swap_buffers فراخوانی می‌شود، HAR این مراحل را دنبال می‌کند:

  1. محتویات بافر رسم را به بافر رندر منتقل می‌کند (و در صورت نیاز پیاده‌سازی، یک انتقال میانی به بافر حل و فصل نیز انجام می‌دهد).

  2. تابع glFlush() را در زمینه GL فراخوانی می‌کند و یک نمونه از EGL_SYNC_NATIVE_FENCE_ANDROID برای ردیابی تکمیل GPU ایجاد می‌کند.

  3. یک درخواست اتمی DRM برای جابجایی بافر رندر به صفحه نمایش ایجاد می‌کند. این درخواست شامل FD حصار GPU (که in fence نامیده می‌شود) است تا از نمایش بافر رندر توسط کنترلر نمایشگر قبل از اتمام ترسیم توسط GPU جلوگیری کند.

  4. همزمان یک حصار جدید از DRM (به نام حصار خروجی) درخواست می‌کند تا زمانی که بافر قبلی (بافر جلویی برای فریم قبلی) دیگر روی صفحه نمایش نیست، سیگنال دهد.

  5. درخواست اتمی را با استفاده از پرچم غیر مسدودکننده ثبت می‌کند تا نخ اصلی بتواند ادامه دهد در حالی که زیرسیستم‌های گرافیکی همگام‌سازی می‌شوند.

  6. حصار خروجی جدید را در متن ذخیره می‌کند تا HAR بتواند منتظر بماند تا در شروع فرآیند swap_buffers در فریم بعدی سیگنال آن ارسال شود. این کار مانع از آن می‌شود که GPU به بافری که هنوز نمایش داده می‌شود، متصل شود.

۴.۳ تنظیم حالت مستقیم

HAR با استفاده از زیرسیستم‌های DRM و Kernel Mode Setting (KMS) مستقیماً با هسته تعامل می‌کند تا وضوح صفحه نمایش AAOS SDV را پیکربندی کند و تعاملات با مدیران پنجره مانند SurfaceFlinger (در پیکربندی‌های خاص) را دور بزند و امکان کنترل انحصاری و با اولویت بالا بر سخت‌افزار صفحه نمایش را فراهم کند.

۴.۴ رندر خارجی

HAR از واگذاری رندر عناصر خاص رابط کاربری (که با تگ‌ها در فیگما مشخص می‌شوند) به فرآیندها یا رشته‌های خارجی پشتیبانی می‌کند. این امر برای ادغام صحنه‌های سه‌بعدی پیچیده (به عنوان مثال، تجسم ماشین ego از موتورهایی مانند Kanzi یا Unity) یا سایر محتواهایی که به یک زمینه OpenGL اختصاصی نیاز دارند، مفید است.

۴.۴.۱ اجزای کلیدی

  • HarExternalRenderContext : یک زمینه EGL خارج از صفحه اختصاصی برای سرویس خارجی.
  • SurfacePool : مجموعه‌ای از بافرهای LocalSurface ( Texture به علاوه EGLImage ) را برای بافرینگ دوگانه یا سه‌گانه مدیریت می‌کند.
  • SharedSurfaceExternalImage : یک پوشش thread-safe برای انتقال هندل‌های EGLImage بین سرویس خارجی و رندرکننده اصلی.

۴.۴.۲ گردش کار

گردش کار از این ترتیب پیروی می‌کند:

  1. سرویس خارجی شروع به کار می‌کند و خود را در حلقه‌ساز اصلی ثبت می‌کند و مشخص می‌کند که کدام تگ‌های فیگما (مثلاً #cluster/3d-car ) را رندر می‌کند.

  2. این سرویس منتظر سیگنال‌های RenderStart از looper می‌ماند تا رندر خود را با سیگنال VSYNC نمایشگر هماهنگ کند.

  3. خارج از صفحه نمایش، این سرویس محتوای خود را در یک فریم بافر ارائه شده توسط SurfacePool رندر می‌کند.

  4. این سرویس swap_buffers در context خود فراخوانی می‌کند که pool را می‌چرخاند و فریم تکمیل‌شده را به عنوان نمونه‌ای از SharedSurface در دسترس قرار می‌دهد.

  5. SharedSurface در ExternalImage قرار می‌گیرد و از طریق کانال Rust MPSC به looper ارسال می‌شود.

  6. رندرکننده‌ی اصلی Impeller (فاز ۳) تصویر خارجی را دریافت می‌کند. به جای کپی کردن داده‌های پیکسلی، EGLImage زیرین را مستقیماً به یک بافت متصل می‌کند و آن را به عنوان بخشی از صحنه‌ی اصلی ترسیم می‌کند و به این ترتیب به ترکیب‌بندی بدون کپی دست می‌یابد.

۴.۵ پلتفرم‌های توسعه و آزمایش (har-platform-linux)

برای اهداف توسعه و آزمایش، برنامه‌های HAR می‌توانند محیط‌های دسکتاپ استاندارد لینوکس و تنظیمات بدون سر (headless) را هدف قرار دهند. این پلتفرم‌ها در جعبه‌ی crates/reference/platforms/har-platform-linux پیاده‌سازی شده‌اند.

برخلاف هدف AAOS SDV تولیدی، این پلتفرم‌ها از زیرسیستم direct-rendering har-gl-context برای خروجی نمایش استفاده نمی‌کنند. در عوض، آنها به جعبه‌های استاندارد Rust OpenGL متکی هستند:

  • حالت پنجره‌ای: winit برای مدیریت پنجره و حلقه‌های رویداد و glutin برای ایجاد زمینه‌های OpenGL ES و ادغام با سیستم پنجره‌ای استفاده می‌کند.

  • حالت بدون سر (Headless mode): از جعبه har-gl-context برای ایجاد یک زمینه pbuffer خارج از صفحه با نمایش پیش‌فرض EGL استفاده می‌کند. این امکان رندر کردن در یک بافر خارج از صفحه را بدون نیاز به یک پنجره قابل مشاهده یا دسترسی مستقیم به سخت‌افزار صفحه نمایش فراهم می‌کند، که در درجه اول برای تست خودکار یا پردازش backend استفاده می‌شود.