Mô hình lớp
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. Kết xuất là quá trình thường khó hiểu để chuyển đổi nội dung đại diện 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 hoạt động của quá trình kết xuất để tận dụng thẻ đồ hoạ: thuật ngữ 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 gì? Bài viết này giải thích mô hình cơ bản làm nền tảng cho tính năng tăng tốc phần cứng khi hiển thị nội dung web trong Chrome.
Chú ý quan trọng
Chúng ta đang nói về WebKit và cụ thể hơn là về cổng Chromium của WebKit. Bài viết này trình bày 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 độ chi tiết triển khai này, vì vậy, không có gì đảm bảo rằng mọi nội dung trong bài viết này sẽ áp dụng cho các trình duyệt khác. Tuy nhiên, kiến thức về nội bộ 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 đang thảo luận về một phần cốt lõi của cấu trúc kết xuất 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 khó có thể thay đổi, nhưng không đảm bảo rằng tất cả nội dung đó vẫn sẽ áp dụng trong 6 tháng tới.
Điều quan trọng là bạn phải hiểu rằng Chrome đã có hai đường dẫn kết xuất khác nhau trong một thời gian: đườ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 bài này, tất cả các trang đều đi theo đường dẫn tăng tốc phần cứng trên Windows, ChromeOS và Chrome cho Android. Trên máy Mac và Linux, chỉ những trang cần kết hợp một số nội dung mới đi theo đường dẫn tăng tốc (xem phần bên dưới để biết thêm về những nội dung cần kết hợp), nhưng sớm thôi, tất cả các trang cũng sẽ đi theo đường dẫn tăng tốc đó.
Cuối cùng, chúng ta sẽ xem xét bên trong công cụ kết xuất và xem xét 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 riêng mình, bạn nên tìm hiểu mô hình lớp. Tuy nhiên, bạn cũng dễ dàng tự làm hại mình: các lớp là các cấu trúc hữu ích, nhưng việc tạo ra nhiều lớp có thể gây ra hao tổn trong toàn bộ ngăn xếp đồ hoạ. Bạn đã được cảnh báo trước!
Từ DOM đến màn hình
Giới thiệu về Lớp
Sau khi tải và phân tích cú pháp, trang sẽ được trình bày trong trình duyệt dưới dạng một 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 bản trình bày trung gian không được hiển thị trực tiếp cho nhà phát triển. Quan trọng nhất trong số các cấu trúc này là lớp.
Trong Chrome, thực sự có một số loại lớp khác nhau: RenderLayers chịu trách nhiệm về các cây con của DOM và GraphicsLayers chịu trách nhiệm về các cây con của RenderLayers. Lớp sau đây là lớp thú vị nhất đối với chúng ta ở đây, vì GraphicsLayers là lớp được tải lên GPU dưới dạng hoạ tiết. Từ giờ trở đi, tôi sẽ chỉ nói "lớp" để chỉ GraphicsLayer.
Một chút về thuật ngữ GPU: kết cấu là gì? Hãy coi đây là hình ảnh bitmap được chuyển từ bộ nhớ chính (tức là RAM) sang bộ nhớ video (tức là VRAM, trên GPU). Sau khi chuyển sang GPU, bạn có thể liên kết dữ liệu này với một hình học 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 "lớp phủ" cho các mô hình 3D dạng khung. Chrome sử dụng hoạ tiết để đưa các phần nội dung trang web vào GPU. Bạn có thể ánh xạ hoạ tiết một cách đơn giản đến nhiều vị trí và phép biến đổi bằng cách áp dụng hoạ tiết đó cho một lưới hình chữ nhật rất đơn giản. Đây là cách CSS 3D hoạt động và cũng rất phù hợp để cuộn nhanh – nhưng chúng ta sẽ tìm hiểu thêm về cả hai tính năng này sau.
Hãy xem một vài ví dụ để minh hoạ 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ụ cho nhà phát triển, trong tiêu đề "hiển thị". Công cụ này chỉ đơn giản là làm nổi bật vị trí của các lớp trên màn hình. Hãy bật tính năng này. Các ảnh chụp màn hình và ví dụ này đều được lấy từ Chrome Canary mới nhất, Chrome 27 tại thời điểm viết bài.
Hình 1: Trang một lớp
<!doctype html>
<html>
<body>
<div>I am a strange root.</div>
</body>
</html>

Trang này chỉ có một lớp. Lưới màu xanh dương đại diện cho các thẻ thông tin. Bạn có thể coi các thẻ thông tin này là các đơn vị phụ của một lớp mà Chrome sử dụng để tải từng phần của một lớp lớn 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 riêng
<!doctype html>
<html>
<body>
<div style="transform: rotateY(30deg) rotateX(-30deg); width: 200px;">
I am a strange root.
</div>
</body>
</html>

Bằng cách đặt một thuộc tính CSS 3D trên <div>
để xoay thuộc tính đó, chúng ta có thể xem giao diện của thuộc tính 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 chế độ xem này.
Tiêu chí tạo lớp
Còn thành phần nào khác có lớp riêng? Phương pháp phỏng đoán của Chrome ở đây đã phát triển theo thời gian và sẽ tiếp tục phát triển, nhưng hiện tại, bất kỳ thao tác tạo lớp kích hoạt nào sau đây đều:
- Các thuộc tính CSS chuyển đổi 3D hoặc phối cảnh
- Các phần tử
<video>
sử dụng tính năng giải mã video tăng tốc - Các phần tử
<canvas>
có ngữ cảnh 3D (WebGL) hoặc ngữ 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ó phần tử con có lớp kết hợp (nói cách khác, nếu phần tử có phần tử con nằm trong lớp riêng)
- Phần tử có một phần tử đồng cấp có chỉ mục z thấp hơn và có một lớp kết hợp (nói cách khác, phần tử này được kết xuất trên một lớp kết hợp)
Hệ quả 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 rất hữu ích để di chuyển nội dung 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ê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 vẽ lại. Đây là một điều tốt: việc vẽ lại sẽ mất thời gian mà bạn có thể dành cho những việc khác, chẳng hạn như chạy JavaScript, và nếu thời gian vẽ lâu thì sẽ gây ra hiện tượng giật hoặc chậm trong ảnh động.
Ví dụ: hãy xem chế độ xem này của dòng thời gian trong Công cụ dành cho nhà phát triển: không có thao tác vẽ nào trong khi lớp này xoay qua lại.

Không hợp lệ! Vẽ lại
Tuy nhiên, nếu nội dung của lớp thay đổi, bạn phải vẽ lại lớp đó.
Hình 4: Vẽ lại các 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 người dùng nhấp vào phần tử đầu vào, phần tử xoay sẽ rộng thêm 1px. Điều này sẽ khiến toàn bộ phần tử được bố cục lại và vẽ lại, trong trường hợp này là toàn bộ lớp.
Một cách hay để xem nội dung đang được vẽ là sử dụng công cụ "show paint rects" (hiển thị hình chữ nhật vẽ) trong Công cụ dành cho nhà phát triển, cũng nằm trong tiêu đề "Rendering" (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 cả phần tử ảnh động và nút đều nhấp nháy màu đỏ khi người dùng nhấp vào nút.

Các sự kiện vẽ cũng xuất hiện trong tiến trình của Công cụ dành cho nhà phát triển. Những người đọc tinh mắt có thể nhận thấy có hai sự kiện vẽ ở đó: một sự kiện cho lớp và một sự kiện cho chính nút đó. Nút này được vẽ lại khi thay đổi thành/từ trạng thái nhấn.

Xin 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, mà sẽ cố gắng chỉ vẽ lại phần DOM đã mất hiệu lực. 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. Tuy nhiên, trong nhiều trường hợp khác, sẽ có rất nhiều phần tử DOM trong một lớp.
Câu hỏi tiếp theo hiển nhiên là nguyên nhân khiến một thành phần hiển thị không hợp lệ và buộc phải vẽ lại. Rất khó để trả lời câu hỏi này một cách đầy đủ vì có rất nhiều trường hợp đặc biệt có thể buộc phải vô hiệu hoá. Nguyên nhân phổ biến nhất là làm hỏng DOM bằng cách thao tác với các kiểu CSS hoặc gây ra việc bố cục lại. Tony Gentilcore có một bài đăng trên blog tuyệt vời về nguyên nhân gây ra việc bố cục lại và Stoyan Stefanov có một bài viết trình bày chi tiết hơn về việc vẽ (nhưng bài viết này chỉ đề cập đến việc vẽ chứ không đề cập đến việc kết hợp phức tạp này).
Cách tốt nhất để tìm hiểu xem việc này có ảnh hưởng đến nội dung bạn đang làm hay không là sử dụng các công cụ Tiến trình công cụ dành cho nhà phát triển và Hiển thị hình chữ nhật vẽ để xem liệu bạn có đang vẽ lại khi không muốn hay không, sau đó cố gắng xác định vị trí bạn đã làm hỏng DOM ngay trước khi bố cục/vẽ lại đó. Nếu việc vẽ là không thể tránh khỏi nhưng có vẻ như mất quá nhiều thời gian, 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 trên màn hình như thế nào? Về mặt khái niệm, công cụ này:
- Lấy DOM và chia thành các lớp
- Vẽ từng lớp này một cách độc lập thành bitmap phần mềm
- Tải các tệp này lên GPU dưới dạng hoạ tiết
- Kết hợp nhiều lớp với nhau thành hình ảnh màn hình cuối cùng.
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 một trang web. Nhưng sau đó, bạn có thể sử dụng một số phím tắt cho các khung hình trong tương lai:
- Nếu một số thuộc tính CSS nhất định thay đổi, bạn không cần phải vẽ lại bất kỳ nội dung nào. 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.).
- Nếu một phần của lớp bị vô hiệu, lớp đó sẽ được vẽ lại và tải lên lại. Nếu nội dung của khung hình không thay đổi nhưng các thuộc tính tổng hợp của khung hình đó thay đổi (ví dụ: nội dung được dịch hoặc độ mờ thay đổi), thì Chrome có thể để nội dung đó trên GPU và kết hợp lại để tạo một khung hình mới.
Như đã nói rõ, mô hình kết hợp dựa trên lớp có tác động sâu sắc đến hiệu suất kết xuất. Việc kết hợp có chi phí tương đối thấp khi không cần vẽ gì cả, vì vậy, tránh vẽ 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 hiệu suất kết xuất. Các nhà phát triển tinh tường sẽ xem danh sách trình kích hoạt kết hợp ở trên và nhận ra rằng họ có thể dễ dàng buộc tạo lớp. Tuy nhiên, hãy cẩn thận khi tạo các lớp này một cách mù quáng, vì chúng không miễn phí: chúng chiếm bộ nhớ trong RAM hệ thống và trên GPU (đặc biệt là bị giới hạn trên thiết bị di động) và việc có nhiều lớp này có thể gây ra các chi phí hao tổn khác trong logic theo dõi lớp nào hiển thị. Nhiều lớp cũng có thể thực sự làm tăng thời gian quét nếu các lớp đó lớn và chồng chéo nhiều nơi mà trước đây không có, dẫn đến hiện tượng đô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!
Đó là tất cả thông tin hiện tại. Hãy theo dõi để biết thêm một vài bài viết về các tác động thực tế của mô hình lớp.