رندر سریع در کروم

مدل لایه

تام ویلتزیوس
Tom Wiltzius

مقدمه

برای اکثر توسعه دهندگان وب، مدل اساسی یک صفحه وب، DOM است. رندر فرآیند اغلب مبهم تبدیل این نمایش از یک صفحه به تصویر روی صفحه است. مرورگرهای مدرن در سال‌های اخیر روش رندرینگ را تغییر داده‌اند تا از مزایای کارت‌های گرافیک استفاده کنند: این اغلب به‌طور مبهم به عنوان «شتاب سخت‌افزار» نامیده می‌شود. وقتی در مورد یک صفحه وب معمولی صحبت می شود (یعنی نه Canvas2D یا WebGL)، این اصطلاح واقعاً به چه معناست؟ این مقاله مدل پایه‌ای را توضیح می‌دهد که زیربنای رندر سریع سخت‌افزاری محتوای وب در کروم است.

هشدارهای بزرگ و چرب

ما در اینجا در مورد WebKit صحبت می کنیم، و به طور خاص در مورد پورت Chromium WebKit صحبت می کنیم. این مقاله جزئیات پیاده سازی کروم را پوشش می دهد، نه ویژگی های پلت فرم وب. پلتفرم وب و استانداردها این سطح از جزئیات پیاده‌سازی را تدوین نمی‌کنند، بنابراین هیچ تضمینی وجود ندارد که هر چیزی در این مقاله برای سایر مرورگرها اعمال شود، اما دانش داخلی می‌تواند برای اشکال‌زدایی پیشرفته و تنظیم عملکرد مفید باشد.

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

درک این نکته مهم است که کروم مدتی است که دو مسیر رندر متفاوت دارد: مسیر سخت‌افزاری شتاب‌دهنده و مسیر نرم‌افزار قدیمی‌تر. از زمان نوشتن این مقاله، همه صفحات در Windows، ChromeOS و Chrome for Android مسیر سخت‌افزاری را طی می‌کنند. در مک و لینوکس فقط صفحاتی که برای برخی از محتوای خود به ترکیب بندی نیاز دارند مسیر شتاب را طی می کنند (برای اطلاعات بیشتر در مورد آنچه که به ترکیب بندی نیاز دارد به زیر مراجعه کنید)، اما به زودی همه صفحات در مسیر تسریع شده نیز در آنجا خواهند رفت.

در نهایت، ما به زیر کاپوت موتور رندر نگاه می کنیم و به ویژگی های آن نگاه می کنیم که تأثیر زیادی بر عملکرد دارد. هنگام تلاش برای بهبود عملکرد سایت خود، درک مدل لایه می‌تواند مفید باشد، اما به راحتی می‌توانید به خودتان شلیک کنید: لایه‌ها ساختارهای مفیدی هستند، اما ایجاد تعداد زیادی از آنها می‌تواند سربار را در سراسر پشته گرافیکی معرفی کند. خود را از قبل هشدار داده شده در نظر بگیرید!

از DOM تا صفحه نمایش

معرفی لایه ها

هنگامی که یک صفحه بارگیری و تجزیه می شود، در مرورگر به عنوان ساختاری که بسیاری از توسعه دهندگان وب با آن آشنا هستند نشان داده می شود: DOM. با این حال، هنگام رندر کردن یک صفحه، مرورگر یک سری نمایش‌های میانی دارد که مستقیماً در معرض توسعه‌دهندگان قرار نمی‌گیرند. مهمترین این ساختارها یک لایه است.

در کروم در واقع چندین نوع لایه مختلف وجود دارد: RenderLayers که مسئول زیردرخت های DOM هستند و GraphicsLayers که مسئول زیردرخت های RenderLayer هستند. مورد دوم در اینجا برای ما بسیار جالب است، زیرا GraphicsLayers همان چیزی است که به عنوان بافت در GPU آپلود می شود. من فقط از اینجا به بعد "لایه" را به معنای GraphicsLayer می گویم.

کنار سریع اصطلاحات GPU: بافت چیست؟ آن را به عنوان یک تصویر بیت مپ در نظر بگیرید که از حافظه اصلی (یعنی RAM) به حافظه ویدیویی (یعنی VRAM، روی GPU شما) منتقل شده است. وقتی روی GPU قرار گرفت، می‌توانید آن را به یک هندسه مش نگاشت کنید - در بازی‌های ویدیویی یا برنامه‌های CAD، از این تکنیک برای دادن پوست به مدل‌های سه‌بعدی اسکلتی استفاده می‌شود. کروم از بافت‌ها استفاده می‌کند تا تکه‌هایی از محتوای صفحه وب را به GPU برساند. بافت ها را می توان با استفاده از آنها روی یک مش مستطیلی ساده به صورت ارزان در موقعیت ها و دگرگونی های مختلف ترسیم کرد. CSS سه‌بعدی اینگونه کار می‌کند، و همچنین برای پیمایش سریع عالی است - اما بعداً در مورد هر دوی این موارد بیشتر خواهد شد.

بیایید به چند مثال برای نشان دادن مفهوم لایه نگاه کنیم.

یک ابزار بسیار مفید هنگام مطالعه لایه‌ها در کروم، پرچم "نمایش مرزهای لایه ترکیبی" در تنظیمات (به عنوان مثال نماد دندانه کوچک) در Dev Tools، تحت عنوان "رندر" است. خیلی ساده محل قرار گرفتن لایه ها روی صفحه را مشخص می کند. بیایید آن را روشن کنیم. این اسکرین شات ها و نمونه ها همگی از آخرین کروم قناری، کروم 27 در زمان نگارش این مقاله گرفته شده اند.

شکل 1: یک صفحه تک لایه

<!doctype html>
<html>
<body>
  <div>I am a strange root.</div>
</body>
</html>
اسکرین شات لایه ترکیبی حاشیه‌های اطراف لایه پایه صفحه را ارائه می‌کند
اسکرین شات لایه ترکیبی حاشیه‌های اطراف لایه پایه صفحه را ارائه می‌کند

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

شکل 2: یک عنصر در لایه خودش

<!doctype html>
<html>
<body>
  <div style="transform: rotateY(30deg) rotateX(-30deg); width: 200px;">
    I am a strange root.
  </div>
</body>
</html>
تصویری از حاشیه های رندر لایه چرخانده شده
تصویری از حاشیه های رندر لایه چرخانده شده

با قرار دادن یک ویژگی CSS سه بعدی در <div> که آن را می‌چرخاند، می‌توانیم ببینیم که وقتی یک عنصر لایه خودش را می‌گیرد، چگونه به نظر می‌رسد: به مرز نارنجی توجه کنید، که یک لایه را در این نمای مشخص می‌کند.

معیارهای ایجاد لایه

چه چیز دیگری لایه خودش را می گیرد؟ اکتشافی کروم در اینجا در طول زمان تکامل یافته است و همچنان ادامه دارد، اما در حال حاضر هر یک از ایجاد لایه ماشه زیر:

  • تبدیل 3 بعدی یا پرسپکتیو خواص CSS
  • عناصر <video> با استفاده از رمزگشایی سریع ویدئو
  • عناصر <canvas> با زمینه سه بعدی (WebGL) یا زمینه دوبعدی تسریع شده
  • پلاگین های ترکیبی (به عنوان مثال فلش)
  • عناصر با انیمیشن CSS برای شفافیت یا استفاده از تبدیل متحرک
  • عناصر با فیلترهای CSS تسریع شده
  • عنصر دارای یک نسل است که دارای یک لایه ترکیبی است (به عبارت دیگر اگر عنصر دارای یک عنصر فرزند باشد که در لایه خودش باشد)
  • عنصر دارای یک برادر با شاخص z کمتر است که دارای یک لایه ترکیبی است (به عبارت دیگر در بالای یک لایه ترکیبی رندر می شود)

مفاهیم کاربردی: انیمیشن

ما می‌توانیم لایه‌ها را هم جابه‌جا کنیم، که آنها را برای انیمیشن بسیار مفید می‌کند.

شکل 3: لایه های متحرک

<!doctype html>
<html>
<head>
  <style>
  div {
    animation-duration: 5s;
    animation-name: slide;
    animation-iteration-count: infinite;
    animation-direction: alternate;
    width: 200px;
    height: 200px;
    margin: 100px;
    background-color: gray;
  }
  @keyframes slide {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(120deg);
    }
  }
  </style>
</head>
<body>
  <div>I am a strange root.</div>
</body>
</html>

همانطور که قبلا ذکر شد، لایه ها برای جابجایی در محتوای وب استاتیک واقعا مفید هستند. در حالت اولیه، کروم محتویات یک لایه را قبل از آپلود آن در GPU به عنوان یک بافت، در یک بیت مپ نرم افزاری رنگ می کند. اگر آن محتوا در آینده تغییر نکند، نیازی به رنگ آمیزی مجدد نیست. این یک چیز خوب است: رنگ‌آمیزی مجدد زمان می‌برد که می‌توان آن را صرف کارهای دیگر، مانند اجرای جاوا اسکریپت کرد، و اگر رنگ طولانی باشد باعث ایجاد مشکل یا تاخیر در انیمیشن‌ها می‌شود.

به‌عنوان مثال، این نمای جدول زمانی Dev Tools را ببینید: در حالی که این لایه به جلو و عقب می‌چرخد، هیچ عملیات رنگی وجود ندارد.

اسکرین شات از جدول زمانی Dev Tools در طول انیمیشن
اسکرین شات از جدول زمانی Dev Tools در طول انیمیشن

نامعتبر! رنگ آمیزی مجدد

اما اگر محتوای لایه تغییر کند، باید دوباره رنگ شود.

شکل 4: رنگ آمیزی مجدد لایه ها

<!doctype html>
<html>
<head>
  <style>
  div {
    animation-duration: 5s;
    animation-name: slide;
    animation-iteration-count: infinite;
    animation-direction: alternate;
    width: 200px;
    height: 200px;
    margin: 100px;
    background-color: gray;
  }
  @keyframes slide {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(120deg);
    }
  }
  </style>
</head>
<body>
  <div id="foo">I am a strange root.</div>
  <input id="paint" type="button" value="repaint">
  <script>
    var w = 200;
    document.getElementById('paint').onclick = function() {
      document.getElementById('foo').style.width = (w++) + 'px';
    }
  </script>
</body>
</html>

هر بار که عنصر ورودی کلیک می شود، عنصر چرخان 1 پیکسل گسترده تر می شود. این باعث رله و رنگ آمیزی مجدد کل عنصر می شود که در این حالت یک لایه کامل است.

یک راه خوب برای دیدن آنچه در حال رنگ‌آمیزی است، استفاده از ابزار "show paint rects" در Dev Tools است، همچنین تحت عنوان "Rendering" تنظیمات Dev Tools. پس از روشن کردن آن، توجه کنید که عنصر متحرک و دکمه هر دو با کلیک روی دکمه قرمز می شوند.

عکس صفحه چک باکس show paint rects
عکس صفحه چک باکس show paint rects

رویدادهای رنگ در جدول زمانی Dev Tools نیز نشان داده می‌شوند. خوانندگان تیزبین ممکن است متوجه شوند که دو رویداد رنگ در آنجا وجود دارد: یکی برای لایه، و دیگری برای خود دکمه، که وقتی به/از حالت افسرده خود تغییر می کند دوباره رنگ می شود.

تصویر صفحه زمانی Dev Tools در حال رنگ آمیزی مجدد یک لایه
تصویر صفحه زمانی Dev Tools در حال رنگ آمیزی مجدد یک لایه

توجه داشته باشید که Chrome همیشه نیازی به رنگ آمیزی مجدد کل لایه ندارد، بلکه سعی می کند فقط بخشی از DOM را که باطل شده است دوباره رنگ آمیزی کند. در این مورد، عنصر DOM که ما اصلاح کردیم، اندازه کل لایه است. اما در بسیاری از موارد دیگر، عناصر DOM زیادی در یک لایه وجود خواهد داشت.

یک سوال واضح بعدی این است که چه چیزی باعث بی اعتباری و مجبور به رنگ آمیزی مجدد می شود. پاسخ کامل به این مشکل دشوار است زیرا موارد لبه زیادی وجود دارد که می‌تواند باعث بی اعتباری شود. شایع‌ترین علت کثیف کردن DOM با دستکاری سبک‌های CSS یا ایجاد relayout است. تونی جنتیلکور یک پست وبلاگ عالی در مورد آنچه باعث تغییر شکل می شود دارد، و استویان استفانوف مقاله ای دارد که نقاشی را با جزئیات بیشتری پوشش می دهد (اما فقط با نقاشی به پایان می رسد، نه این چیزهای ترکیبی فانتزی).

بهترین راه برای فهمیدن اینکه آیا روی چیزی که روی آن کار می کنید تأثیر می گذارد یا نه این است که از ابزار Dev Tools Timeline و Show Paint Rects استفاده کنید تا ببینید آیا زمانی که دوست دارید این کار را نمی کردید دوباره رنگ می کنید یا نه، سپس سعی کنید محل را کثیف کنید. DOM درست قبل از آن relayout/repaint. اگر نقاشی اجتناب ناپذیر است، اما به نظر می رسد که به طور غیرمنطقی طول می کشد، مقاله ابرهارد گرتر در مورد حالت نقاشی مداوم در Dev Tools را بررسی کنید.

کنار هم قرار دادن آن: DOM to Screen

بنابراین چگونه کروم DOM را به تصویر صفحه تبدیل می کند؟ از نظر مفهومی، آن:

  1. DOM را می گیرد و آن را به لایه ها تقسیم می کند
  2. هر یک از این لایه ها را به طور مستقل در بیت مپ های نرم افزاری رنگ می کند
  3. آنها را به عنوان بافت در GPU آپلود می کند
  4. لایه های مختلف را با هم در تصویر نهایی صفحه ترکیب می کند.

همه اینها باید در اولین باری که کروم یک فریم از یک صفحه وب تولید می کند اتفاق بیفتد. اما پس از آن می تواند چند میانبر برای فریم های آینده داشته باشد:

  1. اگر برخی از ویژگی های CSS تغییر کند، نیازی به رنگ آمیزی مجدد نیست. Chrome فقط می‌تواند لایه‌های موجود را که قبلاً روی GPU به عنوان بافت قرار دارند، اما با ویژگی‌های ترکیبی متفاوت (مثلاً در موقعیت‌های مختلف، با کدورت‌های متفاوت و غیره) دوباره ترکیب کند.
  2. اگر بخشی از یک لایه باطل شود، دوباره رنگ شده و دوباره آپلود می شود. اگر محتوای آن ثابت بماند اما ویژگی‌های ترکیبی آن تغییر کند (مثلاً ترجمه شود یا شفافیت آن تغییر کند)، Chrome می‌تواند آن را روی GPU بگذارد و دوباره ترکیب کند تا یک قاب جدید بسازد.

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

فعلاً همین است. منتظر چند مقاله دیگر در مورد مفاهیم کاربردی مدل لایه باشید.

منابع اضافی