• Thứ Tư, 19/05/2010 07:37 (GMT+7)

    3D nguồn mở: Xưa & Nay

    Nguyễn Lê
    Trình điều khiển đồ họa 3D vốn là nguyên nhân của những phàn nàn về các hệ thống nguồn mở. Điều đó giờ đã khác?

    Hầu hết người dùng Unix hay máy tính nói chung hẳn còn nhớ cái thời chưa có màn hình đồ họa. Họ làm việc với thiết bị đơn giản giống như máy telex, chỉ có màn hình văn bản và bàn phím.

    Khoảng chục năm sau các máy trạm đồ họa đầu tiên xuất hiện. Phần cứng đồ họa trong các bộ máy này đơn giản chỉ là vùng nhớ khung hình. Mỗi điểm (pixel) trên màn hình tương ứng 1 địa chỉ ô nhớ. Vì giá bộ nhớ quá cao, màn hình thời đó thường dùng bảng màu thay vì màu thực (mỗi địa chỉ ô nhớ chứa 1 giá trị màu). Thường cách này cần 3byte cho mỗi pixel (24-bit màu), một số phần cứng hiện nay cần nhiều byte hơn. Với bảng màu, mỗi pixel chứa 1 chỉ số tra bảng lưu giá trị màu thực. Với màn hình 16 màu thì mỗi pixel chỉ cần 4bit nhớ.

    Phần cứng hiển thị sẽ đọc lần lượt từng giá trị pixel và cung cấp các giá trị này (hay các giá trị màu được tính bằng cách tìm theo chỉ số trong bảng màu) cho bộ chuyển từ số (digital) sang tương tự (analog) để điều khiển ống đèn hình. Sau đó, khi bộ nhớ rẻ hơn, card đồ họa có bộ nhớ kép trở nên phổ biến, cho phép bạn lấy 1 khung hình trong bộ nhớ trong khi vẽ 1 khung hình khác, rồi tráo đổi chúng với nhau.

    Nếu từng viết trò chơi cho MS-DOS, có thể bạn đã lập trình phần cứng như thế. Trình điều khiển (driver) cho màn hình như thế rất đơn giản. Nó chỉ cần gửi vài lệnh đến phần cứng để thiết lập độ phân giải, rồi sau đó chỉ việc ánh xạ vùng nhớ khung hình thành vùng địa chỉ của tiến trình nào đó để ghi trực tiếp các điểm ảnh lên màn hình.

    Tăng tốc đồ họa

    Phần cứng đồ họa đơn giản trên đôi khi đi cùng tính năng tăng tốc. Một trong những bộ tăng tốc phổ biến nhất là BitBlit do Dan Ingalls đưa ra, thoạt đầu xuất hiện trên hệ thống Xerox Alto để làm cho Smalltalk chạy nhanh. Cách thức hoạt động rất đơn giản: Dùng 1 lệnh duy nhất sao chép một dãy địa chỉ bộ nhớ vào vùng nhớ khung hình. Biến thể của cách này, được biết đến với thuật ngữ “tăng tốc sprite”, gán một mã nhận diện duy nhất cho một vùng nhớ để có thể vẽ lên màn hình rất nhanh.

    Bước phát triển tiếp theo, phần cứng đồ họa được thiết kế để chạy các hệ thống cửa sổ nhanh chóng (hệ thống cửa sổ có giao diện đồ họa, có thể tạo nhiều ô cửa sổ để chạy nhiều ứng dụng đồng thời). Đa phần hệ thống cửa sổ vẽ rất nhiều đường, vì vậy phần cứng được xây dựng có các lệnh để vẽ đường, không phải nhờ đến CPU.

    Trên các máy trạm Unix, chức năng tăng tốc luôn gắn với những tác vụ vẽ cơ bản trong hệ thống cửa sổ. Khi vẽ 1 đường thẳng trong X11, thường bạn thực hiện lệnh gọi hàm Xlib XDrawLine(). Lệnh này gửi thông điệp DrawLine đến server (hệ thống quản lý giao diện đồ họa) để vẽ. Nếu có thể thực hiện, tác vụ này sẽ đưa ra một lệnh đến phần cứng đồ họa để vẽ.

    Các driver thời đó thường phức tạp hơn nhiều. Mỗi chip đồ họa có thể xử lý một tập lệnh vẽ X11 nào đó và phần cứng đồ họa đơn giản không tăng tốc bất kỳ lệnh nào. Driver phải tự kiểm soát những lệnh mà nó có thể tăng tốc và “ra tay” khi có lệnh đặc biệt cần gia tốc bằng phần mềm.

    Một trong những thay đổi trong mô hình driver của X server hiện nay là bỏ qua hầu hết công việc trên. Việc tăng tốc 2D, ít nhất với vẽ đường và các tác vụ tương tự, giờ không còn thích hợp. Thực hiện các việc này hoàn toàn bằng phần mềm, thậm chí trên máy tính khá cũ, vẫn đủ nhanh và quan trọng hơn, thường nhanh hơn dùng phần cứng 2D (các GPU - bộ xử lý đồ họa hiện nay không có thành phần này).

    Tiến lên 3D

    Khi 2D đã nhanh, người ta bắt đầu nghĩ đến tăng tốc 3D. Cụ thể, SGI đã đưa ra dòng máy trạm 3D thường được trình diễn trong các bộ phim vào thập niên 1990, nổi tiếng nhất trong đó là Jurassic Park.

    Trên hầu hết hệ thống UNIX, bạn tương tác với phần cứng 3D thông qua OpenGL thông qua X11. Điều này có nghĩa driver 3D liên quan mật thiết với X11. OpenGL được xây dựng dựa trên IrixGL của SGI và được thiết kế để làm việc chặt chẽ với X11. Như X11, OpenGL cho phép truyền lệnh qua mạng.

    Thoạt đầu OpenGL dùng mô hình luồng tiến trình cố định. Đầu tiên bạn thiết lập các đa giác, sau đó áp bề mặt lên rồi chiếu sáng, cuối cùng hiện lên màn hình. Bộ tăng tốc đồ họa hiện thực một số phần của tiến trình này, phần còn lại giao cho CPU thực hiện.

    Phần đầu tiên được tăng tốc là đổ vân bề mặt. Đây là tác vụ rất tốn bộ nhớ, vì mỗi đa giác đều cần lập thang bitmap để vẽ lên. Rồi phần cứng đẩy nhanh toàn bộ tiến trình. OpenGL đầu tiên cho hệ Xfree86 là giải pháp hoàn toàn bằng phần mềm có tên gọi Mesa. Mesa có khả năng chạy các ứng dụng OpenGL, nhưng vì không dùng bất kỳ phần cứng tăng tốc nào nên nó rất chậm. Một thời gian sau Mesa mới có được kết hợp với dòng card đồ họa 3dfx VooDoo để tăng tốc việc dựng toàn màn hình.

    Việc thông qua X server để dựng 3D ảnh hưởng đến hiệu suất, vì việc này yêu cầu ứng dụng sao chép dữ liệu đến X server, rồi server mới gửi đến card đồ họa. Thao tác sao chép thừa này rất chậm với các bề mặt lớn, vì vậy kỹ thuật dựng hình trực tiếp - DRI (Direct Rendering Infrastructure) được đưa ra.

    Dùng DRI, ứng dụng trực tiếp sinh ra lệnh cho phần cứng đồ họa. Các lệnh này được hệ thống kiểm tra và chuyển đến phần cứng mà không đi qua X server. Tất cả những gì X server cần làm là thiết lập các vùng trên màn hình.

    Lúc này, nhiều người bắt đầu nghĩ đến việc áp dụng các tính năng mới của GPU cho máy tính để bàn thông thường. Thiết kế truyền thống của các hệ thống như X11 dựa trên cơ sở RAM rất đắt. Ví dụ, mỗi khi sắp xếp lại các cửa sổ, hệ thống bảo ứng dụng vẽ lại các bit mới hoàn toàn. Nếu RAM, đặc biệt là video RAM, rẻ hơn, khi đó hệ thống có thể lưu bản sao của cửa sổ trong bộ nhớ và chỉ sao chép dữ liệu từ bản lưu này.

    Việc lưu bản sao của tất cả cửa sổ trong RAM còn có một số lợi ích khác. Khi bạn tập hợp chúng lại để tạo ra ảnh cuối, bạn có thể thực hiện việc biến hình bất kỳ.

    Khá ngạc nhiên, một trong những tác vụ yêu cầu BXL nhiều nhất trong hệ thống cửa sổ là dựng văn bản. Mỗi ký tự trên màn hình được lưu trong 1 tập tin font gồm tập hợp các đường cong Bezier, sau đó được chuyển thành ảnh và được vẽ (thường có khử răng cưa) lên cửa sổ. X server tăng tốc tác vụ này thông qua thư viện XRender. Khi ứng dụng dùng thư viện này, nó dựng các nét thành các ảnh lưu tạm trên server, sau đó dùng phần cứng tổng hợp lại.

    Có khá nhiều mã lệnh thừa. Trình điều khiển OpenGL và driver được dùng để tăng tốc XRender hầu như độc lập. Mặc dù chúng cung cấp tính năng tương tự nhưng có ít mã lệnh dùng chung.

    Không chỉ tăng tốc đồ họa

    Trong vài năm qua, có một từ chiếm lĩnh thị trường GPU (Graphics Processor Unit): bộ tạo bóng. Đó là một chương trình nhỏ chạy trên GPU. Hai thư viện (API) DirectX 11 và OpenGL 3.1 hiện tại đều là bộ tạo bóng. Trong khi các card đồ họa cũ tập trung vào việc thực hiện các phần việc của luồng OpenGL, các card mới tập trung vào việc cho phép nhà phát triển thay thế các phần này. Với các API mới nhất, không hề có luồng chức năng cố định mà chỉ có một loạt chương trình tạo bóng trước tiên làm việc với các điểm chóp sau đó đến các phần dưới.

    Ý nghĩ cho rằng CPU là BXL đa năng còn GPU là BXL chức năng cụ thể có chút hiểu nhầm. Trong thực tế không hề có BXL nào là đa năng. Khi thiết kế một BXL, người ta bắt đầu với việc phác thảo chương trình ứng dụng mà nó sẽ chạy và tìm tần xuất của các dạng lệnh khác nhau. Sau đó người ta phân bổ mật độ transistor tỷ lệ với tần xuất lệnh. Dự án RISC của đại học Berkeley là một trong những hình mẫu đầu tiên của việc này. Các nhà nghiên cứu của Berkeley đã phát hiện mã lệnh sinh ra bởi trình biên dịch có xu hướng dùng một tập lệnh nhỏ, vì vậy họ có thể đơn giản hóa bộ giải mã lệnh bằng cách ưu tiên hỗ trợ tập lệnh con này. Họ cũng nhận thấy các lệnh gọi hàm rất thông dụng trong mã lệnh được biên dịch từ các ngôn ngữ cấp cao.

    Một quy luật hàng đầu duy trì từ thời RISC đến nay là đa phần mã lệnh gần như cứ 7 lệnh có 1 lệnh rẽ nhánh. Đó là lý do tại sao người ta bỏ nhiều công sức cho việc tiên đoán rẽ nhánh. Dạng mã lệnh thường chạy trên GPU có các đặc tính rất khác. Đôi khi nó không có bất kỳ lệnh rẽ nhánh nào, và nếu có thì thường chỉ vài trăm lệnh riêng biệt. GPU hiện tại là bộ xử lý đa năng giống như CPU, chỉ có điều nó được tối ưu theo thuật toán khác.

    Yêu cầu cho trình điều khiển phần cứng như vậy rất khác. Với các GPU chức năng cố định cũ, cách đơn giản nhất là xây dựng driver để cung cấp giải pháp OpenGL bằng phần mềm và dùng chương trình gửi các lệnh thích hợp đến phần cứng thay cho những phần được thực hiện bằng phần cứng. Điều này không ít thì nhiều cũng xảy ra với các trình điều khiển DRI. Tuy không hoàn hảo, vì có nhiều mã lệnh trùng giữa các driver, nhưng nó làm việc được.

    Việc trùng mã lệnh nằm ở phần giám sát trạng thái OpenGL. Như phần lớn API đồ họa, OpenGL có môi trường chung cho các lệnh. Bạn điều chỉnh trạng thái – ví dụ thay đổi màu hiện tại – sau đó gửi đi một số lệnh khác. Phần cứng có thể có hoặc không có trạng thái bên trong tương ứng. Nếu phần cứng không có, khi đó driver phải giám sát trạng thái OpenGL. Trong từng driver không cần hiện thực OpenGL đầy đủ, nhưng có nhiều mã lệnh phải dùng chung.

    Mặt trái của việc trùng mã lệnh là buộc chặt driver vào mô hình OpenGL. Như đã đề cập ở trên, những tính năng như XRender được lợi rất nhiều từ việc tăng tốc, việc này cũng quan trọng với các driver theo hướng OpenGL hiện nay.

    Mô hình mới

    Mọi thứ thay đổi với kiến trúc Galium 3D mới được kết hợp vào Mesa gần đây. Thông thường, khi thiết kế một thư viện hàm driver, bạn tạo ra một dạng thiết bị tổng quát và yêu cầu tất cả thiết bị cụ thể phải dùng giao tiếp chung này. Có thể xem driver như đoạn mã làm cho một phần cứng cụ thể nào đó trở nên giống một thiết bị ảo tổng quát.

    Ví dụ, bộ điều khiển đĩa cứng là một khối thiết bị được thiết kế để hỗ trợ các tác vụ như “lưu khối dữ liệu này ở vị trí kia” và “đọc một khối dữ liệu từ vị trí nào đó”. Driver sẽ cung cấp các hàm này bằng cách đưa ra lệnh SCSI hay IDE đến bus PCI. Driver đồ họa cũng không khác. Khi card được thiết kế để tăng tốc một API có chức năng cố định, cần có driver cung cấp thứ gì đó giống như phiên bản đơn giản của OpenGL hay Direct3D.

    Gallium có cùng ý tưởng, nhưng hiện đại hơn. Thay vì xem phần cứng như một thành phần đồ họa đặc biệt, nó xem phần cứng như một bộ đồng xử lý được thiết kế để chạy các chương trình được viết theo kiến trúc TGSI (Tungsten Graphics Shader Infrastructure), một định dạng nhị phân tổng quát cho các chương trình tạo bóng.

    Thiết bị ảo mà trình điều khiển Gallium hiện thực vẫn có trạng thái trong, phản ánh chính xác hơn trạng thái mà phần cứng giám sát. Nó không gắn chặt với một API đồ họa cụ thể. Điều này có nghĩa bạn có thể gắn các bộ giám sát trạng thái khác nhau để hiện thực các API khác nhau.

    Một trong những API đầu tiên được hiện thực dùng Mesa là OpenGL 2.1. API này làm gọn đi rất nhiều giao tiếp giữa Mesa và các trình điều khiển DRI. Thay cho tập giao tiếp phức tạp giữa Mesa và từng driver trước đây, giờ bạn có 1 giao tiếp phức hợp giữa Mesa và bộ giám sát trạng thái Gallium và 1 giao tiếp đơn giản giữa bộ giám sát trạng thái Gallium và driver.

    Việc này tiện lợi cho các nhà phát triển driver, vì họ không cần biết nhiều về cách OpenGL làm việc và có thể tạo driver đơn giản hơn nhiều. Điều hấp dẫn nhất đó là nó cho phép gắn vào các bộ giám sát trạng thái khác, cho các API khác, dễ dàng hơn rất nhiều.

    Mới đây, Gallium đã có thêm bộ giám sát trạng thái cho các API OpenGL SE 1.1 và 2.0. Đây là phiên bản chức năng cố định và dựa trên cơ chế tạo bóng của các API từ OpenGL thường thấy trong các thiết bị cầm tay. Còn có một API cho OpenVG – thư viện đồ họa vectơ 2D được dùng để tăng tốc Flash và SVG.

    Việc thêm hỗ trợ OpenGL 3.1 cho Gallium dường như dễ hơn nhiều so với việc thêm vào các driver hiện có. Một dự án khác có thể được lợi từ việc này là WINE. Hiện tại, giải pháp WINE của Direct3D biến các lệnh Direct3D thành OpenGL. Việc này không phải luôn suôn sẻ. Tuy cả hai API đều thực hiện cùng chức năng, nhưng chúng có lớp tổng quát khác nhau và không phải luôn tương thích với nhau. Việc hiện thực bộ giám sát trạng thái Direct3D lên Gallium cho phép bạn chạy bất kỳ phần mềm Direct3D nào mà không phải “với” qua OpenGL.

    Mặt khác, Gallium còn có một tập giao tiếp khá gọn cho hệ điều hành (HĐH) và hệ thống cửa sổ. Các giao tiếp này chịu trách nhiệm về những việc như quản lý bộ nhớ và tạo lập môi trường. Mục đích nhằm cho phép tái sử dụng phần lớn mã lệnh của driver trên mọi hệ thống như Linux, BSD và cả Windows.

    Phần sau này rất quan trọng. Một trong những vấn đề thường được đổ cho việc không phát triển driver trên các HĐH nguồn mở là do chi phí. Nếu HĐH chỉ có 1% thị phần, lợi nhuận thu được từ driver sẽ rất nhỏ. Tuy nhiên, nếu chi phí chuyển driver sang HĐH đó rất thấp thì lợi nhuận thu được hấp dẫn hơn.

    Độc giả quan tâm đến lĩnh vực này hẳn còn nhớ Tungsten Graphics, công ty đứng đằng sau kiến trúc Gallium, đã được VMWare mua lại cách đây ít lâu. Có thể bạn thấy lạ là một công ty phát triển driver đồ họa lại trở thành mục tiêu thôn tính của một công ty phát triển hệ thống ảo. Nhưng nhìn vào kiến trúc Gallium bạn có thể hiểu lý do.

    Đặc tính chung giữa các lớp trong Gallium cho phép dễ dàng thêm vào driver để chuyển hướng các lệnh đến máy ảo khác. Miễn là máy khách chạy các ứng dụng 3D và có phần cứng được Gallium hỗ trợ, các ứng dụng này có thể được tăng tốc. Tất nhiên, không chỉ VMWare hưởng lợi từ việc này; Christopher Smowton tại Cambridge đã hiện thực một thiết kế giống như vậy trên Xen.

    Driver hay compiler?

    THUẬT NGỮ

    OpenGL - Open Graphics Library

    Thư viện hàm hay giao tiếp lập trình ứng dụng (API) trong lĩnh vực máy tính, dùng để định nghĩa hình ảnh 2D và 3D.

    X Window System (gọi tắt là X)

    Hệ thống cửa sổ trên hệ Unix, do học viện công nghệ MIT đưa ra lần đầu tiên vào năm 1984. Phiên bản hiện tại thứ 11 (X11) xuất hiện vào tháng 9/1987.

    Như đã nói ở trên, driver là một đoạn mã làm cho thiết bị vật lý giống như một máy ảo (có tính tổng quát). Điều này cũng đúng với trình biên dịch (compiler). Bất kỳ ngôn ngữ lập trình nào cũng đều đưa ra một mô hình tổng quát hóa cách máy tính làm việc, và chính trình biên dịch chịu trách nhiệm hiện thực mô hình trừu tượng này lên kiến trúc thực.

    Với card đồ họa hiện nay, lằn ranh giữa driver và compiler thậm chí còn mơ hồ hơn. Nhiều lệnh mà bạn đưa đến card đồ họa thật sự là các chương trình. Các chương trình này thường được viết bằng ngôn ngữ như GLSL của OpenGL, HLSL của Direct3D hay thậm chí ngôn ngữ không “dính” đến đồ họa như OpenCL. Đầu tiên, trình điều khiển Gallium chuyển các lệnh này sang kiến trúc TGSI, sau đó được chuyển thành lệnh để chạy trên card.

    Một số trình điều khiển Gallium còn thêm một máy ảo khác: LLVM (Low Level Virtual Machine). Đây là một hệ thống trình biên dịch cung cấp một tập lệnh ảo đơn giản, phần lớn việc tối ưu hóa chạy trên tập lệnh này, và một bộ sinh mã tạo ra mã máy cho các kiến trúc khác nhau.

    Việc dùng LLVM cho đồ họa không phải là ý tưởng mới. OS X 10.5 của Apple đã có trình biên dịch GLSL dựa trên LLVM dùng cho các máy không có phần cứng hỗ trợ tạo bóng.

    Về mặt lý thuyết, không nhất thiết LLVM chỉ được dùng cho mã lệnh thay thế dựa trên CPU. Miễn là có hệ thống bên dưới sinh mã cho LLVM để tạo ra tập lệnh cho GPU là bạn có thể sử dụng tất cả tính năng tối ưu của LLVM. Còn có một vài lợi ích hấp dẫn khác, chẳng hạn khả năng biên dịch bất kỳ ngôn ngữ nào có giao tiếp LLVM với GPU. Dĩ nhiên, không thể mong đợi mã lệnh C bất kỳ chạy tốt trên GPU, nhưng các thành phần giải mã video thiên về lệnh số học vectơ hơn là lệnh rẽ nhánh có điều kiện, hầu như chắc chắn có thể chuyển thành chương trình tạo bóng mà không tốn nhiều công sức.

    Không phải là việc của HĐH?

    Thành phần sau cùng là mô-đun dựng hình trực tiếp - DRM (Direct Rendering Module). Thành phần này chịu trách nhiệm cung cấp tập giao tiếp “bất chấp” HĐH cho thành phần của driver trong X server. Thông thường, DRM thật sự là 2 mô-đun: một cung cấp các bit theo HĐH và một thực hiện kiểm tra lệnh theo thiết bị.

    Gần đây, đã có bước chuyển một ít việc cho HĐH. Phần thứ nhất là thiết lập chế độ. Hiện tại, X thay đổi tình trạng thiết bị bằng cách “nói” trực tiếp với thiết bị. Việc này ảnh hưởng đến vấn đề tiết kiệm năng lượng vì HĐH cần khả năng khôi phục thiết bị trở lại trạng thái làm việc từ trạng thái nghỉ. Nó cũng thừa, vì HĐH cũng cần khả năng thiết lập chế độ thiết bị cho các màn hình ảo. Trao việc này cho HĐH làm đơn giản hóa các vấn đề đáng kể.

    Phần thứ hai là quản lý bộ nhớ. Theo truyền thống, quản lý bộ nhớ là việc của HĐH, nhưng với thiết bị đồ họa tình huống này hơi nhập nhằng. Các thiết bị đầu tiên có bộ nhớ riêng và được driver quản lý. Sau đó đến AGP (bộ tăng tốc đồ họa), các thiết bị có khả năng truy cập bộ nhớ hệ thống một cách nhanh chóng. Với PCIe, sự việc càng rối hơn và một số GPU rẻ tiền chiếm luôn bộ nhớ hệ thống.

    Cách quản lý bộ nhớ của thiết bị trong driver hiện nay đưa đến cùng vấn đề mà chúng ta từng gặp: trùng hàng đống mã lệnh và mã lệnh lộn xộn, HĐH, X server và các driver tất cả đều có mã lệnh làm phần việc nào đó. Vì bộ nhớ là tài nguyên có thể chia sẻ giữa các tiến trình, nơi duy nhất mà nó thực sự có ý nghĩa là ở HĐH. Mô hình Gallium trao trách nhiệm này cho HĐH, mặc dù các giao tiếp chính xác là một phần của lớp hệ thống cửa sổ.

    Tương lai tươi sáng

    Nhiều dự án được đề cập ở đây chỉ mới bắt đầu ổn định. Các nỗ lực phát triển driver NVIDIA và ATi nguồn mở hiện đang tập trung vào việc sử dụng Gallium, có thể sẽ trở thành mô hình driver nòng cốt cho các HĐH nguồn mở.

    Năm 2009, Gallium đã có giám sát trạng thái cho X.org, điều đó có nghĩa là bất kỳ thư viện mở rộng nào được hỗ trợ đều có thể tăng tốc mà X không cần bất kỳ mã lệnh nào theo thiết bị. Gallium cũng có thể được dùng cho việc tăng tốc ứng dụng OpenVG để vẽ hình vectơ 2D nhanh (mặc dù hơi thừa với việc vẽ đường đơn giản, nhưng với các đường Bezier khử răng cưa thì nhanh hơn thực hiện trên GPU).

    Driver đơn giản hơn và hỗ trợ nhiều API phía người dùng hơn, tương lai của tăng tốc đồ họa nguồn mở đầy hứa hẹn.

    Nguyễn Lê
    Nguồn: InformIT

    ID: A1004_84