Hiển thị nhanh trong Chrome

Mô hình lớp

Tom Wiltzius
Tom Wiltzius

Giới thiệu

Đối với hầu hết các nhà phát triển web, mô hình cơ bản của một trang web là DOM. Hiển thị là quá trình thường không rõ ràng để biến cách biểu diễn này của một trang thành hình ảnh trên màn hình. Trong những năm gần đây, các trình duyệt hiện đại đã thay đổi cách kết xuất đồ hoạ để tận dụng lợi thế của thẻ đồ hoạ: điều này thường được gọi một cách mơ hồ là "tăng tốc phần cứng". Khi nói về một trang web thông thường (tức là không phải Canvas2D hoặc WebGL), thuật ngữ đó thực sự có nghĩa là gì? Bài viết này giải thích mô hình cơ bản làm nền tảng cho việc hiển thị nội dung web trong Chrome được tăng tốc phần cứng.

Cảnh báo lớn, béo

Chúng ta đang nói về WebKit ở đây, cụ thể hơn là chúng ta sẽ nói về cổng Chromium của WebKit. Bài viết này đề cập đến thông tin chi tiết về cách triển khai Chrome, chứ không phải các tính năng của nền tảng web. Nền tảng web và các tiêu chuẩn không mã hoá mức độ triển khai chi tiết này. Vì vậy, không có gì đảm bảo rằng bài viết này sẽ áp dụng được cho các trình duyệt khác. Tuy nhiên, kiến thức về nội bộ vẫn có thể hữu ích cho việc gỡ lỗi nâng cao và điều chỉnh hiệu suất.

Ngoài ra, xin lưu ý rằng toàn bộ bài viết này sẽ thảo luận về một phần cốt lõi của cấu trúc hiển thị của Chrome đang thay đổi rất nhanh. Bài viết này chỉ cố gắng đề cập đến những nội dung ít có khả năng sẽ thay đổi, nhưng không có gì đảm bảo rằng tất cả nội dung đó sẽ vẫn áp dụng trong 6 tháng tới.

Bạn cần hiểu rằng đã lâu rồi, Chrome đã có hai đường dẫn kết xuất khác nhau: đường dẫn tăng tốc phần cứng và đường dẫn phần mềm cũ. Tại thời điểm viết tài liệu này, tất cả các trang đều đi vào lộ trình tăng tốc phần cứng trên Windows, ChromeOS và Chrome dành cho Android. Trên Mac và Linux, chỉ những trang cần tổng hợp cho một số nội dung mới đi theo đường dẫn tăng tốc (xem bên dưới để biết thêm về những gì yêu cầu kết hợp), nhưng tất cả các trang cũng sẽ sớm chuyển xuống theo lộ trình được tăng tốc ở đó.

Cuối cùng, chúng ta sẽ tìm hiểu sâu về công cụ kết xuất hình ảnh và xem các tính năng của công cụ này có tác động lớn đến hiệu suất. Khi cố gắng cải thiện hiệu suất của trang web của mình, bạn nên hiểu rõ mô hình lớp, nhưng cũng rất dễ để tự giới thiệu bản thân mình: các lớp là những cấu trúc hữu ích, nhưng việc tạo nhiều lớp như vậy có thể gây ra hao tổn xuyên suốt ngăn xếp đồ hoạ. Hãy xem những điều bạn nhận được từ cảnh báo trước nhé!

Từ DOM đến màn hình

Giới thiệu về Lớp

Khi một trang được tải và phân tích cú pháp, trang đó sẽ được thể hiện trong trình duyệt dưới dạng cấu trúc mà nhiều nhà phát triển web quen thuộc: DOM. Tuy nhiên, khi hiển thị một trang, trình duyệt có một loạt các đại diện trung gian không trực tiếp hiển thị cho nhà phát triển. Quan trọng nhất trong các cấu trúc này là một lớp.

Trong Chrome thực tế có nhiều loại lớp khác nhau: RenderLayer, chịu trách nhiệm về các cây con của DOM và GraphicsLayer chịu trách nhiệm cho các cây con của RenderLayer. Điều thứ hai là thú vị nhất đối với chúng ta ở đây, vì GraphicsLayer là những gì được tải lên GPU dưới dạng hoạ tiết. Từ nay, tôi sẽ chỉ nói "layer" (lớp) với ý nghĩa là GraphicsLayer.

Xin lưu ý nhanh về thuật ngữ GPU: kết cấu là gì? Hãy coi đây là một hình ảnh bitmap được di chuyển từ bộ nhớ chính (tức là RAM) sang bộ nhớ video (tức là VRAM trên GPU của bạn). Sau khi GPU nằm trên GPU, bạn có thể ánh xạ GPU theo hình lưới – trong các trò chơi điện tử hoặc chương trình CAD, kỹ thuật này được dùng để tạo “giao diện” cho các mô hình 3D xương. Chrome sử dụng hoạ tiết để đưa các đoạn nội dung trang web vào GPU. Hoạ tiết có thể được ánh xạ rẻ đến các vị trí và phép biến đổi khác nhau bằng cách áp dụng chúng cho một lưới hình chữ nhật thực sự đơn giản. Đây là cách 3D CSS hoạt động và nó cũng tuyệt vời để cuộn nhanh -- nhưng sẽ tìm hiểu thêm về cả hai cách này sau.

Hãy xem một vài ví dụ để minh hoạ cho khái niệm lớp.

Một công cụ rất hữu ích khi nghiên cứu các lớp trong Chrome là cờ "hiển thị đường viền lớp tổng hợp" trong phần cài đặt (tức là biểu tượng bánh răng nhỏ) trong Công cụ dành cho nhà phát triển, bên dưới tiêu đề "kết xuất". Công cụ này chỉ làm nổi bật những lớp trên màn hình. Hãy bật tính năng đó lên nào. Những ảnh chụp màn hình và ví dụ này đều được lấy từ phiên bản Chrome Canary mới nhất, Chrome 27 tại thời điểm viết bài này.

Hình 1: Trang một lớp

<!doctype html>
<html>
<body>
  <div>I am a strange root.</div>
</body>
</html>
Ảnh chụp màn hình đường viền kết xuất lớp tổng hợp xung quanh lớp cơ sở của trang
Ảnh chụp màn hình đường viền kết xuất lớp kết hợp xung quanh lớp cơ sở của trang

Trang này chỉ có một lớp. Lưới màu xanh dương đại diện cho các ô mà bạn có thể coi là các đơn vị con của một lớp mà Chrome sử dụng để tải các phần của một lớp lớn lên cùng một lúc lên GPU. Chúng không thực sự quan trọng ở đây.

Hình 2: Một phần tử trong lớp của chính nó

<!doctype html>
<html>
<body>
  <div style="transform: rotateY(30deg) rotateX(-30deg); width: 200px;">
    I am a strange root.
  </div>
</body>
</html>
Ảnh chụp màn hình đường viền kết xuất của lớp được xoay
Ảnh chụp màn hình đường viền kết xuất của lớp được xoay

Bằng cách đặt một thuộc tính CSS 3D vào <div> xoay nó, chúng ta có thể xem giao diện của nó khi một phần tử có lớp riêng: hãy lưu ý đường viền màu cam, đường viền này phác thảo một lớp trong khung hiển thị này.

Tiêu chí tạo lớp

Những mục nào khác sẽ có lớp riêng? Các phương pháp phỏng đoán của Chrome ở đây đã phát triển theo thời gian và không ngừng cải tiến, nhưng hiện tại bất kỳ cách tạo lớp trình kích hoạt nào sau đây:

  • Các tài sản CSS biến đổi bối cảnh hoặc 3D
  • Các phần tử <video> sử dụng tính năng giải mã video được tăng tốc
  • Các phần tử <canvas> có bối cảnh 3D (WebGL) hoặc bối cảnh 2D tăng tốc
  • Trình bổ trợ tổng hợp (ví dụ: Flash)
  • Các phần tử có ảnh động CSS cho độ mờ hoặc sử dụng phép biến đổi ảnh động
  • Các phần tử có bộ lọc CSS tăng tốc
  • Phần tử có một phần tử con có một lớp kết hợp (nói cách khác, nếu phần tử có một phần tử con nằm trong lớp của riêng nó)
  • Phần tử có đồng cấp với chỉ mục z thấp hơn có lớp kết hợp (nói cách khác, phần tử được hiển thị ở đầu lớp tổng hợp)

Ý nghĩa thực tế: Ảnh động

Chúng ta cũng có thể di chuyển các lớp xung quanh, điều này rất hữu ích cho ảnh động.

Hình 3: Lớp ảnh động

<!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>

Như đã đề cập trước đó, các lớp (layer) thực sự hữu ích khi di chuyển xung quanh nội dung trên web tĩnh. Trong trường hợp cơ bản, Chrome sẽ vẽ nội dung của một lớp thành bitmap phần mềm trước khi tải lớp đó lên GPU dưới dạng hoạ tiết. Nếu nội dung đó không thay đổi trong tương lai, thì bạn không cần sơn lại. Đây là một điều tốt: việc vẽ lại tốn thời gian dành cho các nội dung khác, như chạy JavaScript và nếu quá trình vẽ dài sẽ gây ra quá nhiều cục bộ hoặc độ trễ trong hoạt ảnh.

Ví dụ: xem chế độ xem dòng thời gian của Công cụ dành cho nhà phát triển này: không có thao tác vẽ trong khi lớp này đang xoay qua lại.

Ảnh chụp màn hình dòng thời gian của Công cụ dành cho nhà phát triển trong ảnh động
Ảnh chụp màn hình dòng thời gian của Công cụ dành cho nhà phát triển trong ảnh động

Không hợp lệ! Sơn lại

Nhưng nếu nội dung của lớp thay đổi, nó phải được sơn lại.

Hình 4: Sơn lại lớp

<!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>

Mỗi khi phần tử đầu vào được nhấp vào, phần tử xoay sẽ rộng hơn 1px. Điều này dẫn đến việc bố trí lại và vẽ lại toàn bộ phần tử, trong trường hợp này là toàn bộ lớp.

Một cách hay để xem nội dung được vẽ là gì bằng công cụ "hiển thị kích cỡ khuôn hình chữ nhật" trong Công cụ dành cho nhà phát triển, cũng trong tiêu đề "Kết xuất" của phần cài đặt Công cụ dành cho nhà phát triển. Sau khi bật, hãy lưu ý rằng phần tử động và nút đều nhấp nháy màu đỏ khi người dùng nhấp vào nút này.

Ảnh chụp màn hình hộp đánh dấu hiển thị kích cỡ khuôn hình chữ nhật
Ảnh chụp màn hình hộp đánh dấu hiển thị hình chữ nhật vẽ lại

Các sự kiện vẽ cũng xuất hiện trong dòng thời gian của Công cụ cho nhà phát triển. Người đọc mắt sắc có thể nhận thấy có hai sự kiện tô màu ở đó: một cho lớp và một cho chính nút nhấn, được vẽ lại khi nó chuyển sang/từ trạng thái trầm cảm.

Ảnh chụp màn hình Dòng thời gian của Công cụ dành cho nhà phát triển đang vẽ lại một lớp
Ảnh chụp màn hình Dòng thời gian của Công cụ dành cho nhà phát triển vẽ lại một lớp

Lưu ý rằng Chrome không phải lúc nào cũng cần vẽ lại toàn bộ lớp, Chrome sẽ cố gắng thông minh trong việc chỉ vẽ lại phần DOM đã bị vô hiệu hoá. Trong trường hợp này, phần tử DOM mà chúng ta đã sửa đổi là kích thước của toàn bộ lớp. Nhưng trong nhiều trường hợp khác, sẽ có nhiều phần tử DOM trong một lớp.

Câu hỏi rõ ràng tiếp theo là nguyên nhân gây ra trường hợp mất hiệu lực và buộc phải vẽ lại. Đây là câu hỏi khó trả lời đầy đủ vì có nhiều trường hợp đặc biệt có thể khiến hệ thống xác nhận không hợp lệ. Nguyên nhân phổ biến nhất là làm hỏng DOM bằng cách thao túng các kiểu CSS hoặc gây ra bố cục lại. Tony Gentilcore có một bài đăng trên blog rất hay về nguyên nhân phải bố trí lại, còn Stoyan Stefanov có một bài viết trình bày chi tiết hơn về việc hội hoạ (nhưng bài này kết thúc chỉ bằng cách vẽ tranh chứ không kết hợp đẹp mắt như thế này).

Cách tốt nhất để tìm hiểu xem nó có ảnh hưởng đến những gì bạn đang thực hiện hay không là sử dụng các công cụ Dev Tools (Dòng thời gian) và Show Paint Rects để xem liệu bạn có đang vẽ lại khi bạn không muốn hay không, sau đó cố gắng xác định nơi bạn đã làm sạch DOM ngay trước khi bố trí lại/sơ đồ lại đó. Nếu việc vẽ tranh là bắt buộc nhưng có vẻ mất nhiều thời gian một cách không hợp lý, hãy xem bài viết của Eberhard Gräther về chế độ vẽ liên tục trong Công cụ dành cho nhà phát triển.

Kết hợp: DOM với màn hình

Vậy Chrome biến DOM thành hình ảnh màn hình như thế nào? Về mặt lý thuyết, công cụ này:

  1. Lấy DOM và chia thành nhiều lớp
  2. Vẽ từng lớp một cách độc lập thành các bitmap phần mềm
  3. Tải chúng lên GPU dưới dạng hoạ tiết
  4. Kết hợp các lớp khác nhau lại với nhau thành hình ảnh màn hình hoàn thiện.

Tất cả những việc đó cần phải xảy ra trong lần đầu tiên Chrome tạo khung của trang web. Tuy nhiên, sau đó, bạn có thể mất một số phím tắt cho các khung hình trong tương lai:

  1. Nếu một số thuộc tính CSS thay đổi, thì bạn không cần phải vẽ lại bất cứ thứ gì. Chrome chỉ có thể kết hợp lại các lớp hiện có đã nằm trên GPU dưới dạng hoạ tiết, nhưng với các thuộc tính kết hợp khác nhau (ví dụ: ở các vị trí khác nhau, với độ mờ khác nhau, v.v.).
  2. Nếu một phần của lớp bị mất hiệu lực, thì phần đó sẽ được vẽ lại và tải lên lại. Nếu nội dung của nội dung vẫn giữ nguyên nhưng các thuộc tính kết hợp thay đổi (ví dụ: nội dung được dịch hoặc thay đổi độ mờ), Chrome có thể để nội dung đó trên GPU và tổng hợp lại để tạo khung mới.

Như bạn đã rõ, mô hình kết hợp dựa trên lớp có ý nghĩa sâu sắc đối với hiệu suất kết xuất. Việc kết hợp là khá rẻ khi không có gì cần sơn, vì vậy, việc tránh sơn lại các lớp là một mục tiêu tổng thể tốt khi cố gắng gỡ lỗi kết xuất hiệu suất. Các nhà phát triển hiểu biết sẽ xem xét danh sách các trình kích hoạt kết hợp ở trên và nhận thấy rằng có thể dễ dàng buộc tạo các lớp. Tuy nhiên, hãy cẩn thận khi tạo chúng vì chúng không miễn phí: chúng chiếm bộ nhớ trong RAM hệ thống và GPU (đặc biệt hạn chế trên thiết bị di động) và việc có nhiều trong số đó có thể gây ra hao tổn khác trong logic theo dõi cái nào có thể nhìn thấy. Nhiều lớp cũng có thể thực sự làm tăng thời gian tạo điểm ảnh nếu các lớp này lớn và chồng chéo lên nhiều nơi mà trước đây chúng chưa từng có, dẫn đến điều mà đôi khi được gọi là "vẽ nhiều lần". Vì vậy, hãy sử dụng kiến thức của bạn một cách khôn ngoan!

Video này đến đây là kết thúc. Hãy chú ý theo dõi một số bài viết khác về các ý nghĩa thực tiễn của mô hình lớp.

Nguồn thông tin khác