2 điểm bởi GN⁺ 2024-02-15 | 1 bình luận | Chia sẻ qua WhatsApp
  • M1 đã mắc kẹt ở OpenGL 4.1 suốt nhiều năm, nhưng driver M1/M2 mới nhất của Fedora Asahi Remix nay đã hỗ trợ đầy đủ OpenGL 4.6OpenGL ES 3.2
  • Driver Linux mã nguồn mở của Asahi là driver đạt chứng nhận tương thích có tên trong danh sách Khronos, không giống driver 4.1 không tương thích của nhà cung cấp, qua đó mở rộng khả năng tương thích với các workload OpenGL hiện đại như Blender
  • OpenGL 4.6 yêu cầu robustness, SPIR-V, clip control, cull distance, compute shaders và transform feedback nâng cấp, nhưng phần cứng M1 không khớp nguyên vẹn với các tiêu chuẩn đồ họa hiện đại
  • Các tính năng không có trong phần cứng được bù bằng kỹ thuật driver và compiler: geometry shaders, tessellation, transform feedback được xử lý bằng compute shaders, còn cull distance và clip control được xử lý bằng biến đổi shader
  • Để vượt qua hơn 100.000 bài kiểm tra tương thích, robustness cho buffer và image được triển khai bằng phần mềm, đồng thời thêm các tối ưu như clamp, preamble optimization và đường vòng mipmap để giảm chi phí phụ trội

Có thể dùng OpenGL 4.6/ES 3.2 trên Fedora Asahi Remix

  • Trước đây M1 chỉ hỗ trợ đến OpenGL 4.1, nhưng giờ đã hỗ trợ OpenGL 4.6OpenGL ES 3.2
  • Driver mới nhất cho dòng M1/M2 có thể dùng bằng cách cài Fedora Asahi Remix
  • Người dùng đã cài sẵn có thể cập nhật bằng lệnh sau
    • dnf upgrade --refresh
  • Driver Mesa mã nguồn mở của Asahi có tính tương thích (conformance) với các phiên bản OpenGL mới nhất, không giống driver 4.1 không tương thích của nhà cung cấp
  • Driver 4.6/3.2 đạt chuẩn phải vượt qua hơn 100.000 bài kiểm tra để bảo đảm tính chính xác
  • Sau khi driver OpenGL ES 3.1 trở thành driver đạt chuẩn đầu tiên cho API đồ họa tiêu chuẩn trên M1 cách đây 6 tháng, lần này OpenGL 4.6 cũng đã hoàn tất
  • Hỗ trợ Vulkan cũng đang được phát triển

Triển khai tính năng để vượt qua rào cản OpenGL 4.1

  • OpenGL 4.6 bổ sung nhiều tính năng bắt buộc so với 4.1
    • Robustness
    • SPIR-V
    • Clip control
    • Cull distance
    • Compute shaders
    • Transform feedback được nâng cấp
  • M1 không thực sự phù hợp với các tiêu chuẩn đồ họa mới hơn OpenGL ES 3.1
    • Vulkan biến một số tính năng thành tùy chọn, nhưng để phân lớp DirectX và OpenGL lên trên nó thì vẫn cần các tính năng còn thiếu
    • Giải pháp hiện có trên M1 không thể vượt quá bộ tính năng của OpenGL 4.1
  • Các tính năng mới không có hỗ trợ phần cứng được triển khai bằng kỹ thuật driver
    • Geometry shaders, tessellation và transform feedback được xử lý bằng compute shaders
    • Cull distance được xử lý bằng các giá trị nội suy đã biến đổi
    • Clip control được triển khai bằng vertex shader epilogue

Buffer robustness và phần bù bằng phần mềm trên M1

  • GPU truyền thống ưu tiên hiệu năng hơn độ an toàn, nên với mã shader sai đọc ra ngoài phạm vi buffer, có thể phát sinh hành vi không xác định
  • Với các ứng dụng xử lý shader không đáng tin cậy như trình duyệt web, kiểu đánh đổi này là không mong muốn
    • Bản thân graphics API không phải là ranh giới bảo mật nên vẫn cần một mức độ sanitization nhất định
    • Giảm hành vi không xác định trong API giúp tăng cường phòng thủ theo chiều sâu
  • Khi bật Robustness, ứng dụng có thể chọn hành vi được xác định cho truy cập ngoài phạm vi, đổi lại là hy sinh một phần hiệu năng
  • Kết quả của thao tác tải buffer ngoài phạm vi khác nhau tùy theo API
    • Direct3D và Vulkan robustBufferAccess2: trả về 0
    • OpenGL và Vulkan robustBufferAccess: trả về 0 hoặc một phần dữ liệu trong buffer
    • OpenGL ES: có thể là giá trị bất kỳ, miễn không bị crash
  • Vì yêu cầu của OpenGL là truy cập ngoài phạm vi phải trả về 0 hoặc dữ liệu nằm trong buffer, nên có thể tính unsigned minimum giữa chỉ số truy cập và chỉ số hợp lệ cuối cùng để tải bằng chỉ số an toàn
    • tải uniform buffer không có robustness: load.i32 result, buffer, index
    • sau khi áp dụng robustness: umin idx, index, last rồi load.i32 result, buffer, idx
  • Preamble của M1 sẽ tính một lần và tái sử dụng thay vì lặp lại cùng một giá trị trên mọi thread
    • Kích thước uniform buffer là cố định nên phần số học robustness bổ sung cũng có thể được chuyển vào preamble
    • Với robust storage buffer, dù bản thân load/store không thể di chuyển, phép tính clamp vẫn có thể được đưa vào preamble

Triển khai vertex buffer robustness

  • Trong graphics API, ứng dụng sẽ thiết lập địa chỉ base GPU của vertex buffer và layout thuộc tính
    • Mỗi thuộc tính có offset và format
    • Buffer có stride, biểu thị số byte trên mỗi vertex
    • Vertex shader ngầm đánh chỉ số thuộc tính theo từng vertex để đọc
  • Một số phần cứng triển khai robust vertex fetch ở mức native hoặc tăng tốc software fetch bằng buffer có kiểm tra biên, nhưng M1 không có cả hai
  • Lệnh tải bộ nhớ trên GPU M1 nhận địa chỉ base 64-bit và offset theo đơn vị phần tử, đồng thời cung cấp lệnh integer multiply-add imad
    • Tải thuộc tính 32-bit có thể được triển khai bằng hai lệnh imad idx, stride/4, vertex, offset/4load.i32 result, base, idx
    • Với thuộc tính vector gồm 4 giá trị 32-bit được bố trí liên tiếp, có thể tải bằng một lệnh load.v4i32 result, base, vertex << 2
  • Robustness cần clamp, nhưng kích thước vertex buffer tính theo byte còn lệnh tải tối ưu lại dùng chỉ số theo vertex
  • Vấn đề được giải bằng cách diễn giải lại nhiều thuộc tính và offset trong cùng một buffer như các địa chỉ base riêng cho từng thuộc tính
    • Thay vì cộng offset trong shader, truyền base riêng cho từng thuộc tính
    • Có thể chuyển kích thước buffer theo byte thành kích thước theo đơn vị vertex cho từng thuộc tính
    • Clamp chỉ số vertex thay vì offset
  • Driver sẽ tính trước chỉ số vertex hợp lệ cuối cùng từ kích thước format của từng thuộc tính rồi truyền vào shader
  • Nếu buffer quá nhỏ đến mức không thể tải bất cứ thứ gì thì clamp không giải quyết được, nên buffer của thuộc tính đó sẽ được thay bằng một zero buffer nhỏ
    • Vì dùng địa chỉ base theo từng thuộc tính nên quyết định này cũng có thể thực hiện riêng cho từng thuộc tính
  • Kết quả là có thể triển khai robust vertex buffer chỉ với một chút tính toán ở driver và chi phí của một lệnh umin

Image robustness và đường vòng mipmap

  • Ngoài buffer robustness còn cần image robustness, và image load ngoài phạm vi phải trả về 0
  • Image có mipmap chứa nhiều level of detail
    • Base level là ảnh gốc
    • Mỗi level tiếp theo là phiên bản thu nhỏ của level trước
    • Khi render, phần cứng sẽ chọn level gần với kích thước hiển thị để cải thiện hiệu quả và chất lượng hình ảnh
  • Đặc tả yêu cầu trong robustness, image load phải trả về 0 trong các trường hợp sau
    • Tọa độ X hoặc Y nằm ngoài phạm vi
    • level nằm ngoài phạm vi
  • Hành vi image load của GPU M1 khác với yêu cầu này
    • Nếu X hoặc Y ngoài phạm vi thì trả về 0
    • Nếu level ngoài phạm vi thì trả về giá trị của level cuối cùng
  • Do nhà cung cấp không công bố tài liệu phần cứng, không rõ đây là chủ ý hay lỗi phần cứng, và cần một cách обход để vượt qua kiểm thử tương thích
  • Cách обход đơn giản là chỉ load khi level hợp lệ, nếu không thì trả về 0 bằng branch, nhưng branch kém hiệu quả
  • Cách tốt hơn là tận dụng việc load ở level ngoài phạm vi không làm crash, nên cứ load trước rồi dùng compare-select để chọn 0
    • Tuy nhiên tập lệnh của GPU M1 là scalar, trong khi image load trả về vector 4 thành phần red/green/blue/alpha
    • Mỗi thành phần cần ulesel, khiến assembly trở nên cồng kềnh
  • Đường vòng cuối cùng tận dụng thực tế là khi X hoặc Y ngoài phạm vi thì phần cứng trả về 0
    • Vì chiều rộng image tối đa là 16384px, đổi X thành giá trị như 20000 sẽ khiến nó ra ngoài phạm vi
    • Nếu level hợp lệ thì dùng X gốc, nếu không hợp lệ thì đổi X thành 20000 để image load trả về 0
  • Cách này chỉ thay đổi một scalar thay vì chọn toàn bộ vector, nên được biên dịch thành assembly gọn hơn
    • Nếu nạp sẵn hằng số vào uniform register thì chi phí của đường vòng chỉ là một lệnh duy nhất
    • Bằng cách này, bài kiểm tra tương thích đã được vượt qua

1 bình luận

 
GN⁺ 2024-02-15
Ý kiến trên Hacker News
  • Alyssa Rosenzweig giống như một món quà lớn vẫn tiếp tục đóng góp cho cộng đồng
    Mỗi bài blog đều chắc chắn khiến tôi học được điều gì đó mà trước đây chưa biết về cấu trúc bên trong của phần cứng đồ họa hiện đại

  • Đây là công việc cho thấy thực lực lần nào cũng thắng lời nói
    Chỉ đọc blog thôi cũng có quá nhiều thứ để nghiền ngẫm đến mức đầu nóng lên, và dù kết luận nằm ở câu thứ hai chứ không phải câu cuối, cuối cùng vẫn bị cuốn theo từng thao tác bit một xuống tận hang thỏ
    Nếu có benchmark kiểu số lần giác ngộ trên mỗi đoạn văn, có lẽ Alyssa sẽ đứng đầu tất cả

  • Nếu một ngày nào đó Apple loại bỏ OpenGL 3.3 core, có lẽ cuối cùng mọi người cũng sẽ loại bỏ theo
    Nói chung tôi nghe rằng OpenGL dễ dùng hơn Vulkan, nhưng những API quá phức tạp có thể khiến các nhà phát triển ít kinh nghiệm khó tận dụng GPU, trở thành rào cản gia nhập và đẩy các nhà phát triển game indie ra ngoài
    Dạo này ai cũng dùng Unity và Unreal nên việc tự làm từ đầu hoặc dùng engine khác trông có vẻ kỳ lạ; sau khi Unity cố siết chặt hơn, việc chứng kiến mảng phát triển game thức tỉnh vừa thú vị vừa khó chịu
    Mã nguồn mở trong phát triển game luôn ở tình trạng chật vật, và dù có Godot, có vẻ vẫn khó đối đầu nghiêm túc với Unity và Unreal
    Ngay cả khi Godot đủ khả thi, các nhà phát triển indie quen với Unity và Unreal hơn nên khả năng cao vẫn sẽ ở lại đó
    Tình trạng mã nguồn mở trong phát triển game đôi khi khiến người ta cảm thấy tuyệt vọng, và sự xuất hiện của các API đồ họa thế hệ mới không làm mọi thứ dễ hơn

    • Nói OpenGL dễ hơn Vulkan là khá có lý
      Ví dụ render tam giác bằng OpenGL dài khoảng 200 dòng, còn ví dụ render tam giác bằng Vulkan dài khoảng 1000 dòng
    • Cá nhân tôi thấy Metal dễ dùng hơn Vulkan
      Vulkan được thiết kế rất linh hoạt, nhưng đổi lại không có nhiều tính năng tiện lợi
      Dù thế nào đi nữa, OpenGL là API quá mức cao cấp để phơi bày trực tiếp như API của driver; đặt một API cấp thấp như Vulkan làm lớp nền rồi đặt thứ gì đó như OpenGL lên trên sẽ phù hợp hơn với cách phần cứng GPU vận hành
      Ngoài ra, không phải ai cũng dùng Unity và Unreal
      Cả 6 đề cử Game of the Year tại The Game Awards 2023 đều được làm bằng engine riêng, và trong giới indie vẫn có các nhà phát triển tự làm engine như Hades
      Tuy vậy, đúng là đa số dùng engine có sẵn
    • OpenGL không bị loại bỏ, mà đơn giản hơn và vẫn tiếp tục được dùng ở những nơi Vulkan là quá mức cần thiết
      Nếu nó đáp ứng mọi chức năng cần thiết và bạn có thể chấp nhận pipeline render dựa trên trạng thái, thì dùng nó cho dự án mới cũng là một lựa chọn tốt
    • Trên macOS và iOS, OpenGL đã ở trạng thái dự kiến loại bỏ từ vài năm trước
      Nó vẫn hoạt động và hiện nay chạy như một lớp trên Metal, nhưng khi build mã GL cho macOS hoặc iOS, cảnh báo loại bỏ vẫn liên tục hiện ra
      Có thể tắt bằng define
    • WGPU gần với hướng giải quyết vấn đề này hơn, như một API đa nền tảng dễ dùng hơn Vulkan
      Vấn đề của OpenGL là nó quá xa cách thức GPU vận hành, nên khó đạt hiệu năng tốt
  • Tôi tò mò bao nhiêu phần trong công việc này bị ràng buộc với mã GPU M1, và bao nhiêu phần triển khai tính năng-chồng-trên-tính-năng có thể tái sử dụng ở nơi khác
    Nó trông rất giống cách Zink chạy các tính năng OpenGL phức tạp trên Vulkan thô sơ hơn, nhưng M1 hiện vẫn chưa có backend Vulkan để làm mục tiêu

    • Nói rộng hơn, OpenGL hoặc Vulkan phức tạp có thể chạy trên bất kỳ tổ hợp nào giữa render phần mềm trên CPU và hỗ trợ tăng tốc native theo từng phần cứng
      Cuối cùng đây là vấn đề khối lượng công việc, và có thể được tái sử dụng cho nhiều loại phần cứng
      Nó cũng có thể hữu ích cho phần cứng đã cũ, được hiểu rõ nhưng khó dùng độc lập cho workload hiện đại
  • Tôi rất tò mò tác động hiệu năng do việc này gây ra là bao nhiêu, đặc biệt khi so với dùng trực tiếp Metal trên macOS
    Câu trả lời chắc chắn sẽ là “còn tùy”, nhưng tôi vẫn tò mò
    Có thể câu trả lời nằm trong bài, nhưng tôi không hiểu phần lớn nội dung

    • Không nhất thiết phải có khác biệt lớn giữa việc triển khai một tính năng bằng mã compute trong driver và bằng hỗ trợ phần cứng GPU
      “Hỗ trợ phần cứng” thường cũng được triển khai bằng microcode GPU và trong nhiều trường hợp đi qua cùng phần silicon
      Bất kỳ tính năng nào cũng có thể trở thành nút thắt hiệu năng, và trước khi thực sự thử thì rất khó biết chỗ nào sẽ nghẽn
    • Cách Alyssa diễn đạt khiến tôi thấy hơi lạ
      Đúng là GPU Apple không hỗ trợ native geometry shader, nhưng geometry shader có thiết kế không tốt và không hợp với phần cứng GPU
      Ngay cả trên phần cứng được nói là có hỗ trợ thực sự, nó cũng nổi tiếng là chậm, và có lý do để Nvidia thiết kế mesh shading
      Transform feedback cũng thường được nhắc đến, nhưng GPU Apple có thể ghi vào vị trí bộ nhớ tùy ý ở bất kỳ giai đoạn shader nào, nên transform feedback về cơ bản là không cần thiết
      Điểm cốt lõi là Apple đã triển khai một kiến trúc compute gọn gàng, đồng thời cắt bỏ nhiều di sản cũ và những tính năng vốn được biết là hoạt động không tốt
      Cách nói “M1 bị mắc kẹt ở OpenGL 4.1” có vẻ không phù hợp
      Tôi đã không theo dõi OpenGL từ lâu nên không biết họ đang nói tới tính năng nào sau 4.1, nhưng nếu có tính năng làm được trong OpenGL mà không làm được trong Metal thì tôi sẽ rất ngạc nhiên
      Ngược lại, có rất nhiều việc làm được trong Metal nhưng hoàn toàn không thể trong OpenGL; việc Metal Shading Language có con trỏ đầy đủ đã là một ví dụ
  • Cái này là dành cho Fedora trên M1
    Sẽ rất đáng ngạc nhiên nếu cũng làm được trên macOS, nhưng tôi tò mò không biết cần những gì để tạo ra thứ như vậy

    • Suy cho cùng đây là cấu trúc tạo command buffer rồi gửi đến GPU, nên cần có cách làm việc đó trên macOS
      Driver Mesa ban đầu cho GPU M1 cũng được bootstrap bằng cách gửi command buffer tới driver AGX của macOS qua IOKit
      https://rosenzweig.io/blog/asahi-gpu-part-2.html
      https://github.com/AsahiLinux/gpu/blob/main/demo/iokit.c
      Vì vậy phía Mesa cần thêm một ít mã “keo” để chuyển surface của GPU thành đối tượng có thể được compositing lên màn hình macOS
    • Theo các nhà phát triển, thực tế việc này khó vì Apple không có API kernel công khai ổn định: https://social.treehouse.systems/@AsahiLinux/111930744188229065
    • Có lẽ đã có thể làm được qua đường MoltenVK → Vulkan → Zink chăng
    • Có vẻ Apple cấm driver kernel bên thứ ba
      Tôi hiểu rằng để tạo một triển khai Vulkan hay OpenGL đúng nghĩa thì cần một phần đối ứng phía kernel phụ trách xử lý GPU
      Có lẽ vì thế mà dường như không có ai cố triển khai Vulkan native cho macOS
      Tuy nhiên nếu có thể làm trên driver của Apple thì tôi không rõ
    • Có thể triển khai driver OpenGL trên Metal
      Nhưng tôi không biết liệu có lý do gì để bỏ nhiều tài nguyên như vậy cho một API legacy không tối ưu hay không
  • Gọi việc đổi truy cập ngoài phạm vi từ trap sang trả về dữ liệu tùy ý là robustness thì khá buồn cười
    Lập trình đồ họa đúng là kỳ lạ

    • Nếu nhìn từ góc độ viết driver đồ họa thì điều đó có lý, và cũng phù hợp với luật Postel, tức nguyên tắc robustness
      Cốt lõi của driver GPU là chạy được các ứng dụng bị lỗi hoặc chạy chúng nhanh hơn
      Việc đặt mặc định nghiêm ngặt sẽ không sửa được vấn đề mang tính cấu trúc của ngành game, nơi mã hỏng vẫn được phát hành; nó chỉ khiến người dùng rời đi
      Trên phần cứng nơi phân nhánh thường rất đắt đỏ, một flag cho hệ thống biết hãy âm thầm xử lý các trường hợp biên theo cách hiệu quả nhất có vẻ hữu ích
      Có lẽ cũng có nhiều trường hợp sử dụng hợp lệ, nơi lập trình viên có thể tin tưởng một cách hợp lý rằng các trường hợp biên đó hầu như không ảnh hưởng đến frame render cuối cùng
    • Không có kiểm tra robustness không có nghĩa là truy cập ngoài phạm vi nhất định sẽ bị trap, nên robustness ở đây có nghĩa là cung cấp kết quả đã biết ngay cả trong những trường hợp lạ như vậy
      Kết hợp với việc GPU nói chung không thích trap cho lắm thì điều này hợp lý
      Carmack cũng từng nói rằng khi thiết kế megatexture, việc thuyết phục các nhà sản xuất chấp nhận ý tưởng bộ nhớ ảo là rất đau khổ
    • Vì vậy trong các lĩnh vực đồ họa, điện toán hiệu năng cao, vật lý năng lượng cao và giao dịch siêu tốc, tương lai của C và C++ vẫn rất sáng
      Nói về các ngôn ngữ lập trình khác trong những lĩnh vực bị chi phối bởi văn hóa “hiệu năng quan trọng hơn an toàn” cũng giống như nói với bức tường
  • Rõ ràng đây là chuyện rất thú vị, nhưng tôi thắc mắc vì sao họ không nhắm tới Vulkan trước
    Ngày nay nó có vẻ là mục tiêu quan trọng hơn, và bên trên nó đã có triển khai OpenGL rồi

    • Lớp tương thích OpenGL trên Vulkan không phải phép màu
      Để hỗ trợ một tính năng OpenGL cụ thể, driver Vulkan phải hỗ trợ tính năng tương ứng, và thường cần extension
      Nói cách khác, chỉ triển khai driver Vulkan cơ bản không tự nhiên đem lại hỗ trợ OGL 4.6 miễn phí; để Mesa dịch OGL 4.6 sang Vulkan thì driver Vulkan phải triển khai toàn bộ tính năng OGL 4.6
      Hơn nữa Alyssa là người đã từng làm reverse engineering và dự án driver OpenGL
      Tôi không biết chi tiết, nhưng khả năng cao là làm driver cho API quen thuộc sẽ dễ và nhanh hơn nhiều so với làm driver cho API chưa quen
    • Trước hết họ nhắm tới OpenGL cũ để làm cho bộ tính năng cơ bản hoạt động
      Từ đó nâng lên OpenGL mới hơn có khả năng là ít việc hơn so với một triển khai Vulkan hoàn chỉnh, và có lẽ họ cũng đã học được nhiều thứ cần cho Vulkan
    • Tôi cũng từng nghĩ tương tự, nhưng để hỗ trợ OpenGL trên Vulkan thì dù sao cũng cần phiên bản Vulkan cao hơn và khối lượng công việc cũng lớn
      Vì vậy có vẻ họ đã chọn bắt đầu từ phiên bản OpenGL thấp hơn để có được thứ gì đó chạy được nhanh hơn
  • Khá đáng kinh ngạc khi nghĩ rằng nếu John Carmack trong thập niên 90 không khăng khăng dùng OpenGL trong Quake II, có lẽ OpenGL đã không có được sự hiện diện trong game 3D

    • Quake có lẽ chỉ là một phần nhỏ trong lịch sử
      Thứ cuối cùng đã tạo nên OpenGL như ta biết là SGI cùng nỗ lực khổng lồ nhằm tạo ra các bản triển khai tương thích trên nhiều hệ thống và kiến trúc
    • Một sự thật thú vị: trang OpenGL lâu đời nhất còn được lưu trữ là một banner FAST GAMES GRAPHICS khổ lớn, kèm đồ họa Quake 1 động và một menu khác :-P
      https://web.archive.org/web/19970707113513/http://www.opengl.org/
    • Không rõ đó có phải lý do duy nhất không, nhưng việc Carmack ủng hộ OpenGL chắc chắn đã giúp ích
      Rất nhiều thứ liên quan đến game 3D có được là nhờ Doom và Quake
    • Vài năm sau, vào năm 2011, John Carmack nói rằng Direct3D khi đó là API tốt hơn
      Ông cho rằng Microsoft có đủ can đảm để tiếp tục thực hiện những thay đổi lớn không tương thích nhằm cải thiện API, còn OpenGL thì bị kìm hãm vì lo ngại về tính tương thích
      Ông nói Direct3D xử lý đa luồng tốt hơn, và các phiên bản mới hơn cũng quản lý trạng thái tốt hơn
      Dù vậy, ông nói id Software vẫn ở lại với OpenGL là do quán tính, và dù Direct3D có ưu điểm, họ không có kế hoạch chuyển sang Direct3D
      Nguồn: https://www.bit-tech.net/news/gaming/pc/carmack-directx-better-opengl/1/
    • Tài liệu bối cảnh: https://www.chrishecker.com/OpenGL/Press_Release