Board 500 ảnh đã hết giật khi kéo
Khi một board có hàng trăm ảnh, kéo hay zoom bị khựng, nhất là trên . Trang này viết cho người không làm kỹ thuật và kể lại từ đầu: trình duyệt dựng board từ những gì, vì sao chỗ đó làm chậm, mình đã sửa bằng cách nào, và số đo trước, sau. Chữ có gạch chân chấm là thuật ngữ: rê chuột hoặc chạm vào để xem giải thích ngay tại chỗ.
Số đo
1Thử kéo board
Mô phỏng dưới đây không phải board thật, nhưng chạy theo số đo thật. Board tự trượt qua lại như khi bạn cầm chuột kéo. Ở chế độ Trước, mô phỏng cố tình bỏ đúng bằng tỉ lệ đã đo trên board thật, nên bạn thấy nó khựng y như trên máy. Ở chế độ Sau, nó bỏ khung theo số đo sau khi sửa.
Cách xem: chọn và 500 ảnh, rồi bấm qua lại giữa Trước và Sau. Để ý ba ô bên dưới: số , độ bận, và dải ô đỏ. Đổi sang 50 hay 200 ảnh để thấy board càng nhiều ảnh thì càng nặng.
Mỗi viền mờ quanh ảnh là một lớp mà trình duyệt phải xếp lại trong từng khung hình.
2Số đo trước và sau
Đo thế nào: cùng một máy, cùng board 500 ảnh xếp thành lưới đều. Máy tự gửi 240 lần cuộn chuột để kéo và zoom như người thật, rồi ghi lại thời gian trình duyệt làm từng việc. Mỗi bên đo 3 lần, lấy số ở giữa (), để một lần đo xui không làm lệch kết quả.
Trang đưa số đo lên trước. Cột "Sau" là kết quả của giải pháp ở phần 9. Các phần 3 tới 8 giải thích từng con số này nghĩa là gì và vì sao nó cao như vậy.
"Trước" là tắt, tức cách vẽ cũ. "Sau" là công tắc bật, có . "" là cùng máy đó nhưng người thợ chính bị làm chậm 4 lần. Dưới mỗi chỉ số có một dòng nói con số đó nghĩa là gì.
| Đo cái gì | Trước | Sau | So sánh | Giảm | Mục tiêu |
|---|
Trước: 89808e8ed, công tắc tắt, trung vị 3 lần đo. Sau: d3f1c1163, công tắc bật, trung vị 3 lần, hai phiên đo nên ghi thành khoảng; cột Giảm tính theo số tệ hơn của khoảng. Số lớp đo trên board 750 ảnh ở 8bf9cd172. Máy Apple M4, Chrome 156, màn 1080p.
Lý thuyết
3Một giây có 60 khung hình
Màn hình máy tính thay hình 60 lần mỗi giây, giống một cuốn sách lật thật nhanh. Mỗi hình gọi là một . Để bạn thấy board trượt mượt theo tay, mỗi khung phải là một hình mới, board nằm ở vị trí mới hơn khung trước một chút.
Một giây chia cho 60 thì mỗi khung có khoảng 16,7 (mili giây, một phần nghìn giây) để trình duyệt chuẩn bị xong. Kịp thì hình mới hiện ra. Không kịp thì màn hình không chờ: nó giữ nguyên hình cũ thêm một nhịp, và khung đó coi như . Lúc hình mới cuối cùng xuất hiện, board đã nhảy một đoạn xa hơn bình thường.
Thỉnh thoảng bỏ một khung thì mắt không nhận ra. Khi hơn nửa số khung bị bỏ, board đi theo kiểu đứng, nhảy, đứng, nhảy. Đó là cái giật bạn thấy. Dải dưới đây là một giây thật trên , trước khi sửa.
Một giây kéo board 500 ảnh trên máy yếu, trước khi sửa: khoảng 39 trên 60 khung bị bỏ.
4"Trình duyệt bận 873 ms mỗi giây" nghĩa là gì
Trình duyệt chạy gần như mọi việc của một trang web trên một . Hãy hình dung đó là một người thợ duy nhất: chạy code của Gavana, tính chỗ cho từng card, , và nhận cú kéo chuột của bạn. Người thợ này chỉ làm được một việc tại một thời điểm.
Con số "bận" đếm xem trong mỗi giây (1.000 ), người thợ đó phải làm việc bao nhiêu ms. Bận 300 nghĩa là làm 300 ms, nghỉ 700 ms: thoải mái. Bận 873 nghĩa là chỉ còn 127 ms rảnh. Chia cho 60 thì mỗi khung chỉ dư khoảng 2 ms. Chỉ cần một khung nặng hơn bình thường một chút là trễ hạn.
Vì sao bận lại gây giật? Mỗi khung có hạn chót cứng 16,7 ms, giống một chuyến xe buýt chạy đúng giờ, cứ 16,7 ms một chuyến. Người thợ chưa làm xong hình mới khi xe tới thì hình đó lỡ chuyến: màn hình giữ hình cũ, khung đó . Hình mới phải chờ chuyến sau.
Thêm nữa, người thợ không nhận việc mới khi đang dở việc cũ, nên cú kéo chuột của bạn phải xếp hàng chờ. Bận gần 1.000 nghĩa là hàng chờ không bao giờ hết: board phản ứng chậm hơn tay bạn, lúc đứng lúc nhảy. Đó là cảm giác giật.
Bấm ba tình huống dưới đây để xem. Mỗi thanh xanh là thời gian người thợ làm một khung. Mỗi vạch dọc là một chuyến xe: xanh lá là kịp, đỏ là lỡ.
Dòng thời gian là mô hình đơn giản, phóng to 200 ms đầu của một giây kéo. Việc mỗi khung lấy từ số đo: 873 ms ÷ 60 khung ≈ 14,5 ms cho máy mạnh; 234 ÷ 60 ≈ 3,9 ms sau khi sửa. Với , mỗi khung cần quá một nhịp, nên mô hình đặt khoảng 40 ms để khớp với tỉ lệ đo được 65% khung bị bỏ. Số 873 là lần đo ngày 30/10 có tách thời gian theo từng bước (dùng ở phần 5). Bảng ở phần 2 ghi 908 vì đó là 3 lần của đợt đo cuối. Hai số cùng một tình huống: kéo board 500 ảnh trên máy mạnh, cách vẽ cũ.
5Mỗi khung hình đi qua năm bước
Để ra được một , trình duyệt làm năm bước theo thứ tự, như một dây chuyền. Bốn bước đầu do người thợ duy nhất () làm. Bước cuối giao cho , một con chip riêng chuyên vẽ. Bước nào chậm thì cả dây chuyền chậm theo.
Tính vị trí camera
Code của Gavana tính xem bạn đã kéo tới đâu và báo cho trình duyệt.
Luồng chínhXếp chỗ cho card
Tính kích thước và vị trí của từng hộp trên trang.
Luồng chínhGhi lệnh vẽ
Ghi ra danh sách cần vẽ gì, ở đâu. Chưa tô điểm ảnh nào.
Luồng chínhXếp lớp
Quyết định thứ gì thành riêng, lớp nào đè lớp nào.
Luồng chínhGhép và hiện
Card đồ hoạ chồng các lớp lại thành hình trên màn hình.
Card đồ hoạBiểu đồ dưới cho thấy trong một giây kéo board 500 ảnh trên máy mạnh, người thợ tiêu thời gian vào bước nào. Vạch đứt ở cuối là 1.000 , tức bận kín.
Bản sau mới đo tổng, chưa tách theo từng bước. Hai phiên đo cho 93 và 234 ms.
Đọc biểu đồ: code của Gavana (Script) chỉ tốn 108 ms. Tính chỗ gần như không tốn gì. Bước xếp lớp chiếm 633 ms, gần ba phần tư tổng thời gian bận. Nghĩa là board chậm không phải vì code của mình, cũng không phải vì tô ảnh, mà vì trình duyệt phải sắp xếp quá nhiều lớp. Muốn nhanh thì phải giảm số lớp.
Phần 6 tả board đang được dựng thế nào. Phần 7 giải thích vì sao lại có nhiều lớp đến vậy.
Hiện trạng
6Canvas gồm những gì
Màn hình board trông phẳng như một tờ giấy. Bên trong, trình duyệt dựng nó thành nhiều chồng lên nhau, giống những tấm phim trong suốt xếp chồng trên bàn đèn. Mỗi tấm được vẽ một lần rồi giữ lại. Sau đó chỉ việc chồng các tấm lên nhau để ra hình bạn thấy.
Chia lớp có lợi: khi bạn kéo board, trình duyệt chỉ cần trượt các tấm, không phải vẽ lại từng ảnh. Nhưng có giá: trong mỗi , trình duyệt vẫn phải kiểm lại danh sách các tấm, tấm nào nằm trên tấm nào. Mười tấm thì việc này nhanh. Hàng trăm tấm thì nó thành việc nặng nhất, như phần 5 đã cho thấy.
Board 500 ảnh lúc đứng yên chỉ có 10 lớp, chia thành bốn nhóm dưới đây. Bấm các nút để xem lúc kéo thì số lớp thay đổi ra sao.
- 1Nền và lưới chấm. Màu nền và các chấm lưới giúp bạn biết mình đang ở đâu trên board. Lưới thay đổi theo mức zoom, nên trong lúc kéo hay zoom nó được tính lại ở mỗi khung hình.
- 2Đường nối. Các sợi dây nối card với nhau, vẽ bằng SVG (một cách vẽ hình bằng đường nét). Khi bạn kéo, trình duyệt tách lớp này ra riêng để trượt cho nhanh. Chính việc tách riêng này dẫn tới vấn đề ở phần 7.
- 3Các card. Mọi card nằm chung trên một tấm nền lớn. Kéo board là trình duyệt dời cả tấm nền, không dời từng card, giống kéo cả tấm bảng ghim đầy ảnh. Nhưng mỗi card trông như một ô ảnh lại được ghép từ nhiều mảnh nhỏ mà trình duyệt phải theo dõi riêng: khung, ảnh, tên, viền, nhãn. Zoom xa, mỗi card khoảng 11 mảnh, cả board 500 ảnh là 5.747 mảnh. Zoom gần, card hiện thêm ô gõ prompt và các nút, lên khoảng 37 mảnh, cả board là 18.747 mảnh.
- 4Thanh công cụ, menu, khung chọn. Luôn nằm trên cùng và không đổi khi bạn kéo board, nên gần như không tốn gì.
Bấm "Đang kéo, trước" để xem điều gì xảy ra với lớp số 3.
Vì sao mỗi phần của card lại là một mảnh riêng?
Trình duyệt không biết "card" là gì. Nó chỉ biết các hộp, mỗi hộp có kích thước, vị trí, màu và có thể nhận cú bấm riêng. Card là do code của Gavana ghép nhiều hộp lại, giống một mô hình Lego mà trình duyệt chỉ thấy từng viên.
Mỗi phần phải là một hộp riêng vì nó làm một việc riêng: ảnh co giãn theo zoom, tên là chữ thật để sửa và chọn được, viền đổi màu khi rê chuột hay khi chọn, nút phải bấm được, ô prompt phải gõ được. Gộp hết thành một tấm hình phẳng thì không gõ, bấm hay chọn chữ được nữa.
Cái giá là khi board di chuyển, trong mỗi khung hình trình duyệt phải xem lại từng hộp: to bao nhiêu, nằm đâu, có đè lên hộp nào không. Khi zoom xa thì không ai gõ hay bấm vào card, nên lớp GPU vẽ card thành một tấm hình phẳng. Bạn rê chuột hay bấm vào card nào thì card thật với đủ các hộp quay lại ngay.
Board có sẵn một cách tiết kiệm: card nào nằm ngoài màn hình thì không dựng, giống như không bày đồ ra bàn khi chưa dùng. Khi bạn zoom gần vào một góc, chỉ vài chục card được dựng, nên board vẫn nhanh. Nhưng khi zoom xa để xem toàn board, card nào cũng nằm trong màn hình, nên cả 500 card đều phải dựng. Đó chính là lúc chậm nhất, và cũng là lúc lớp GPU ở phần 9 vào việc.
Mỗi ảnh được lưu sẵn hai bản: bản nhỏ 480 px và bản lớn 1024 px. Card nhỏ trên màn hình thì dùng bản nhỏ cho nhẹ. Phóng to qua một ngưỡng thì đổi sang bản lớn cho nét. Việc đổi bản này là nguyên nhân chậm thứ hai, ở phần 8.
Phân tích
7500 tấm kính
Lúc đứng yên, board chỉ có 10 . Lúc kéo, số lớp vọt lên khoảng 519. Chuyện xảy ra như sau.
Khi bạn kéo, trình duyệt tách lớp đường nối (số 2) ra riêng để trượt cho nhanh. Nhưng các card phải nằm đè lên đường nối, để dây không vắt ngang qua ảnh. Trình duyệt có một quy tắc: thứ gì nằm trên một lớp riêng thì cũng phải thành lớp riêng, nếu không nó không giữ được thứ tự trên dưới. Thế là từng card một bị tách thành một lớp riêng.
Board 500 ảnh vì thế có khoảng 519 lớp, gần như mỗi card một lớp. Trình duyệt phải sắp xếp lại toàn bộ số lớp đó trong mỗi , 60 lần mỗi giây. Bảng dưới cho thấy số lớp tăng đúng theo số ảnh.
Mỗi ảnh là một tấm kính riêng. Trong mỗi khung hình, người thợ (chấm đỏ) phải đi qua từng tấm, kiểm vị trí và thứ tự, rồi mới đưa cho . 500 ảnh là 500 tấm, 60 lần mỗi giây.
Khi zoom xa, mọi ảnh được in sẵn lên một tấm poster do card đồ hoạ giữ. Người thợ không phải đi qua từng ảnh nữa. Kéo board chỉ là bảo card đồ hoạ trượt tấm poster đi.
| Số ảnh trên board | Lớp lúc đứng yên | Lớp lúc đang kéo |
|---|---|---|
| 50 | 10 | 61 |
| 200 | 10 | 217 |
| 500 | 10 | ≈ 519 |
Hai cách sửa rẻ đều làm tệ hơn
Trước khi làm lớp GPU, mình thử hai cách rẻ để bớt lớp, mỗi cách chỉ sửa vài dòng CSS (phần quy định cách trang hiển thị): ẩn lưới chấm nền, và không tách riêng lớp đường nối. Số lớp và thời gian có giảm như mong đợi. Nhưng khung hình khi kéo lại tăng từ 1,5% lên 13,6–15,5%, tức tệ hơn khoảng 10 lần.
Vì sao bớt lớp lại tệ hơn? Một lớp giống một bức tranh đã vẽ xong. Muốn dời nó thì chỉ việc trượt, không cần vẽ lại. Khi card không còn là lớp riêng, cả 500 ảnh bị vẽ chung lên một tấm lớn, cùng với lưới chấm và đường nối. Trong lúc kéo, lưới chấm và đường nối thay đổi theo từng khung. Trên một tấm, chỉ cần một chi tiết đổi là phải tô lại cả tấm, kéo theo cả 500 ảnh nằm trên đó, 60 lần mỗi giây. Giống một bức tranh tường: muốn sửa một nét thì phải sơn lại cả bức.
Ít tấm nên xếp nhanh. Nhưng một chi tiết nhỏ đổi (một chấm lưới, một sợi dây) là phải tô lại cả tấm chứa 500 ảnh. Tốn ở bước tô.
Không phải tô lại, chỉ trượt. Nhưng mỗi khung phải xếp lại 500 tấm. Tốn ở bước xếp lớp. Đây là cách cũ, và đo thấy nó đỡ tệ hơn cách gộp.
Với cách vẽ cũ, chỉ chọn được tốn ở chỗ này hay tốn ở chỗ kia. Lớp GPU thoát khỏi thế kẹt: chỉ có một tấm, nên không phải xếp nhiều lớp. Tấm đó cũng không bao giờ phải tô lại trên , vì ảnh đã nằm sẵn trong bộ nhớ của card đồ hoạ. Mỗi khung, card đồ hoạ tự vẽ lại 500 ảnh nhỏ từ bộ nhớ đó. Với nó đây là việc nhẹ, vì nó được làm ra để tô hàng triệu điểm ảnh cùng lúc.
8Giải mã ảnh lại mỗi lần zoom
Đây là nguyên nhân thứ hai, chỉ xảy ra khi zoom. Ảnh được lưu ở dạng nén để nhẹ. Muốn hiển thị, trình duyệt phải , tức là mở nén thành từng điểm ảnh, giống giải nén một file zip trước khi dùng. Việc này cũng do người thợ chính làm.
Như phần 6 đã nói, mỗi ảnh có hai bản 480 px và 1024 px. Khi bạn zoom, card to lên hay nhỏ đi. Vượt qua ngưỡng là trình duyệt đổi sang bản kia và giải mã ảnh lại từ đầu. Zoom qua lại quanh ngưỡng thì 500 ảnh bị đổi bản rồi đổi lại liên tục. Riêng việc này tốn khoảng 0,8 giây trong mỗi giây zoom: người thợ gần như chỉ ngồi mở file.
Cách sửa đi kèm: trong lúc bạn đang zoom thì không đổi bản ảnh. Đợi bạn dừng tay rồi mới nâng lên bản nét hơn. Khi bật, ảnh đã nằm sẵn trong bộ nhớ card đồ hoạ nên không phải giải mã lại. Kết quả: giải mã khi zoom ra giảm từ 1.036 xuống 0 mỗi giây.
Giải pháp
9Một lớp GPU cho lúc zoom xa
Có một cách sửa triệt để: viết lại toàn bộ board để vẽ hết. Cách đó mất hàng tháng. Nó còn làm hỏng những thứ cần chữ và hộp thật trong trang: gõ prompt, chọn chữ để copy, phần mềm đọc màn hình cho người khiếm thị, và các bài test tự động.
Mình chọn cách nhỏ hơn: thêm một vẽ bằng GPU, và lớp này chỉ làm việc khi zoom xa. Đó đúng là lúc board chậm nhất, vì 500 card cùng hiện. Đó cũng là lúc không ai gõ hay đọc chữ trên card, vì card quá nhỏ. Zoom gần lại thì card thật quay về như cũ.
In ảnh nhỏ thành từng trang
Mỗi ảnh được thu nhỏ về 128 px, đủ nét khi card nhỏ trên màn hình. Rồi ảnh được xếp vào các "trang" trong bộ nhớ card đồ hoạ, 64 ảnh một trang, giống dán ảnh vào album. 500 ảnh vừa 8 trang, tốn 44,7 MB bộ nhớ card đồ hoạ, dưới mức trần 64 MB đặt cho tính năng này.
8 trang × 64 = 512 ô cho 500 ảnh. 12 ô trống ở trang cuối.
Trả lại card thật khi bạn chạm vào
- Rê chuột, chọn, focus hay sửa một ảnh: card thật với đủ nút và chữ quay lại ngay trong cùng , nên bạn không thấy chỗ chuyển.
- Card đã được GPU vẽ thì ẩn khỏi trang, nhưng vẫn bấm được, và phần mềm đọc màn hình vẫn đọc được tên.
- Nếu card đồ hoạ mất bộ nhớ đột ngột, board quay về cách vẽ cũ ngay, rồi in lại các trang khi GPU sẵn sàng. Board không bị trống lúc chuyển.
- Đang zoom thì không đổi bản ảnh (phần 8).
Card đã vẽ rời khỏi việc xếp lớp
Sau khi lớp GPU chạy, đo lại trên board 750 ảnh thì thấy một chỗ sót. Card đã được GPU vẽ thay và phần bên trong đã ẩn, nhưng cái vỏ ngoài của card vẫn bị trình duyệt tính là một lớp riêng. Nghĩa là người thợ vẫn phải xếp hàng trăm lớp không ai nhìn thấy.
Ẩn hẳn cả vỏ card thì số lớp giảm từ 335 xuống 19, thời gian từ 429–640 xuống 8–22 mỗi giây.
10Thuê thêm một người thợ
Lớp ở trên chỉ chạy khi zoom xa. Zoom gần lại thì board quay về cách vẽ cũ, và người thợ chính vẫn gánh mọi việc. Board càng lớn, hàng nghìn card, thì chỗ đó càng nặng.
GAV-289 đi xa hơn: thuê thêm một người thợ thứ hai, gọi là (Web Worker). Người thợ này chạy song song với người thợ chính, cầm card đồ hoạ, và vẽ cả board ở mọi mức zoom. Người thợ chính chỉ còn lo phần bạn đang gõ hay sửa, nên luôn rảnh để nhận cú kéo chuột của bạn.
Biểu đồ dưới so độ bận của người thợ chính khi không có và khi có luồng phụ, trên một board rất lớn: 6.251 card, .
Board 6.251 card, máy yếu
Thời gian bận () trong mỗi giây kéo hoặc zoom. Ở bản luồng phụ, người thợ chính gần như rảnh hẳn, và không quá 2,1%, trên board lớn gấp 12 lần board 500 ảnh.
Tiến độ
11Tiến độ: đã làm tới đâu
Phần này để cuối: những gì đã lên code, những gì đang làm, và những gì phải xong trước khi bật cho mọi người.
Đã xong
- Lớp khi zoom xa (GAV-265) và bỏ vỏ card khỏi việc (GAV-288) đã merge vào
main. Cả hai nằm sau , công tắc đang tắt, nên người dùng chưa thấy gì khác. - , bước 1/6 (GAV-289, !570) merge sáng 06/11: board chỉ xem (trang Admin, board nhúng) được vẽ bằng luồng phụ. Mới bật cho một tài khoản test.
Đang làm
Luồng phụ được chia thành 6 bước, mỗi bước là một merge request riêng, merge được và tắt được độc lập với nhau. Bước 1 đã xong (ở trên). Bước 2 tới 6 đang làm song song: đưa luồng phụ vào board chỉnh sửa, hỗ trợ đủ mọi loại card, rồi bật dần cho người dùng. Bảng dưới là các trình duyệt đã thử với luồng phụ.
Chạy được trên trình duyệt nào
| Chrome, có | Đạt |
| Chrome, chỉ có WebGL2 | Đạt |
| Firefox | Đạt |
| Máy không có GPU | Về cách vẽ cũ |
| Nhân Safari khi zoom vào (bỏ 73–87% khung) | Chưa đạt |
| Safari thật, Edge, Windows, Linux, điện thoại | Chưa đo |
"Về cách vẽ cũ" nghĩa là máy đó vẫn dùng board như hôm nay, chỉ không nhanh hơn. "Chưa đạt" là đã thử và còn giật, phải sửa trước khi bật. "Chưa đo" là chưa thử.
Cần xong trước khi bật cho mọi người
Năm việc dưới đây cần xong trước khi bật cho mọi người. Mỗi việc ghi rõ: hiện trạng, vì sao quan trọng, và bước tiếp theo.
Chưa bật cho người dùng
Lớp GPU đã vào main, tức bản code chính đang chạy trên production, nhưng công tắc đang tắt. Người dùng chưa thấy gì khác: board vẫn vẽ theo cách cũ.
Vì sao quan trọng. Mọi số đo ở trang này làm trên board thử. Bật cho mọi người khi chưa đo trên board thật thì có thể gặp lỗi mà board thử không có.
Bước tiếp. Bật cho tài khoản nội bộ trước, đo trên vài board thật của team, rồi mới bật dần cho người dùng.
Mới đo trên một máy
Mọi số đo làm trên một máy Mac M4. "" là chính máy đó bị làm chậm người thợ chính 4 lần bằng công cụ của Chrome.
Vì sao quan trọng. Một laptop yếu thật thường yếu cả card đồ hoạ, trong khi máy đo vẫn có card đồ hoạ mạnh. Lớp GPU dựa vào card đồ hoạ, nên trên máy yếu thật, kết quả có thể kém hơn số ở đây.
Bước tiếp. Đo lại trên một laptop Windows phổ thông và một máy Mac đời cũ.
Safari khi zoom vào
Ở bản luồng phụ (phần 10), nhân của Safari còn bỏ 73–87% khi zoom gần vào. Nghĩa là trên Safari, zoom vào còn giật hơn cả cách cũ.
Vì sao quan trọng. Một phần người dùng Mac dùng Safari. Bật luồng phụ mà chưa sửa chỗ này thì những người đó thấy tệ hơn.
Bước tiếp. Thử trên Safari thật (bản đo hiện là nhân Safari dùng để test), tìm nguyên nhân. Chưa sửa xong thì Safari dùng cách vẽ cũ.
Board đo là lưới đều
Board đo gồm toàn ảnh, xếp ô vuông đều nhau. Board thật của người dùng trộn ảnh, video, List, ô chữ và đường nối, nằm lộn xộn và đè lên nhau.
Vì sao quan trọng. Video và List có cách vẽ riêng. Đường nối nhiều hơn thì số 2 nặng hơn. Số đo trên board thật có thể khác số ở đây.
Bước tiếp. Đo trên vài board thật của team, cùng cách đo như phần 2.
Trình đọc màn hình
Card do GPU vẽ vẫn giữ tên trong trang, để phần mềm đọc màn hình (người khiếm thị dùng để nghe nội dung trang) đọc được.
Vì sao quan trọng. Đây mới là cách làm theo lý thuyết. Chưa ai bật VoiceOver của Mac lên và nghe thử xem có đọc đúng, đủ, đúng thứ tự không.
Bước tiếp. Nghe thử bằng VoiceOver trên một board có lớp GPU bật, trước khi bật cho người dùng.
Nguồn: docs/research/canvas-gpu-rendering-2026-10.md, _docs/reports/gav-265-measure-final/REPORT.md, plans/gav-288-gpu-overview-all-cards.md, _docs/reports/gav-289-matrix/table.md trong repo gavana.ai.