Thư Viện Bao Bọc BGFX Bằng Lua - nói dối e blog

Thư Viện Bao Bọc BGFX Bằng Lua

Vài năm trước, một người bạn đã giới thiệu tôi về thư viện BGFX. Ấn tượng đầu tiên khiến tôi bị cuốn vào chính là khẩu hiệu của nó: “Bring Your Own Engine/Framework” - một thư viện render theo phong cách độc đáo. Trong thời đại mà cứ mỗi thư viện nhỏ cũng vội vã tự xưng là “engine 3D”, việc tập trung hoàn toàn vào nhiệm vụ cốt lõi - làm một thư viện render chuyên nghiệp - thật sự rất đáng trân trọng.

Vào dịp Quốc khánh năm nay, tôi tình cờ tìm lại dự án này và ngạc nhiên phát hiện tác giả vẫn đang cập nhật đều đặn. Duy trì phát triển một dự án nhỏ bé suốt 5 năm trời đã khiến tôi đặt nhiều niềm tin hơn vào BGFX. Sau kỳ nghỉ, tôi đã dành thời gian nghiên cứu kỹ lưỡng mã nguồn của nó.

Thiết kế của BGFX thực sự hợp gu tôi: phần lõi gần như không chứa bất kỳ thành phần thừa nào. Các chức năng như tính toán dữ liệu, hỗ trợ API nền tảng, định dạng lưu trữ dữ liệu… đều được tách biệt hoàn toàn. Nhiệm vụ duy nhất của nó là tích hợp các API đồ họa đa dạng (DirectX, OpenGL…) thành một giao diện thống nhất, giúp phát triển ứng dụng đồ họa 3D đa nền tảng dễ dàng hơn bao giờ hết. Việc xử lý sự khác biệt giữa các API 3D trên các nền tảng chính là phần việc nặng nhọc và phức tạp nhất trong phát triển game 3D.

Dù đã có một vài thư viện binding Lua cho BGFX, nhưng tôi thấy chúng chưa thực sự “đậm chất Lua”. Hầu hết chỉ là bọc nguyên API C đơn giản, không đầy đủ các chức năng. Vì vậy, tôi đã dành nửa tháng để xây dựng một bộ bao bọc Lua hoàn toàn mới.

Vì BGFX chỉ tập trung vào render mà không xử lý tạo cửa sổ hay nhận input, tôi đã tích hợp thêm thư viện IUP để hỗ trợ giao diện. Nhờ IUP có sẵn các thành phần giao diện bản địa, khi chuyển đổi các ví dụ mẫu từ C++ sang Lua, tôi đã loại bỏ ImGui trong bản gốc và thay thế bằng IUP thuần túy.

Quá trình chuyển đổi từng ví dụ mẫu của BGFX đã giúp tôi hiểu sâu hơn về kiến trúc và triết lý thiết kế API của nó. Kết hợp với kinh nghiệm sử dụng Lua, tôi đã thiết kế các API Lua tương tự nhưng không cứng nhắc bắt chước nguyên bản.

Ví dụ: Trong C/C++, để xây dựng cấu trúc đỉnh (vertex) cần chuỗi API VertexDecl::begin, VertexDecl::add, VertexDecl::end vì giới hạn của ngôn ngữ. Nhưng với Lua, chỉ cần một API duy nhất nhờ vào cấu trúc dữ liệu linh hoạt của nó.

Một điểm khác biệt nữa: Trong C/C++ thường dùng toán tử bit kết hợp macro để tạo các cờ hiệu (flags). Cách này không trực quan trong Lua, nên tôi chuyển sang dùng chuỗi mô tả thay thế.

Lua cũng có những điểm yếu: không thao tác trực tiếp bộ nhớ. Điều này khiến các API liên quan đến địa chỉ bộ nhớ (như cập nhật buffer động) cần được bao bọc lại một cách thông minh. Tôi đã giải quyết vấn đề này bằng cách thiết kế các hàm trợ giúp đặc biệt.

Quy trình phát triển thư viện này của tôi khá đặc biệt: Tôi chọn các ví dụ mẫu tiêu biểu của BGFX và chuyển dịch chúng sang Lua. Trong quá trình này, bất kỳ API C/C++ nào bị phụ thuộc sẽ được tôi lập tức bao bọc thành phiên bản Lua tương ứng. Nhờ đó, thư viện bao bọc phát triển dần dần, hoàn thiện theo từng ví dụ mẫu.

Hiện tại tôi đang phát triển trên Windows 10 64bit với trình biên dịch mingw64. Dù đây là thư viện đa nền tảng, nhưng do hạn chế về thời gian, tôi chưa xây dựng hệ thống build đa nền tảng. Các bạn quan tâm có thể tải trực tiếp phiên bản thư viện động đã biên dịch sẵn từ trang GitHub của tôi.

Lưu ý: Phiên bản Lua chuyển dịch trực tiếp từ C thường không tối ưu, đặc biệt với các ví dụ nặng về tính toán như 02_metaballs - chạy chậm hơn bản C++ rõ rệt. Để tối ưu hiệu năng, cần viết lại theo phong cách Lua và dùng C để xây dựng các thư viện tính toán hiệu năng cao, sau đó gọi từ Lua.

Tuy nhiên, đa số các ví dụ chạy mượt mà không thua kém bản C/C++. Với mục đích thử nghiệm ý tưởng mới hay phát triển prototype, Lua mang lại tốc độ phát triển vượt trội nhờ cú pháp nhẹ nhàng và tính linh hoạt đặc trưng.

0%