RenderScript

RenderScript là một khung để chạy các tác vụ tính toán chuyên sâu với hiệu suất cao trên Android. RenderScript được thiết kế để sử dụng với tính năng tính toán song song dữ liệu (data-parallel computation), mặc dù khối lượng công việc nối tiếp (serial workloads) cũng có thể hưởng lợi. Môi trường thời gian chạy RenderScript tải song song công việc trên các bộ xử lý hiện có trên thiết bị, chẳng hạn như GPU và CPU đa nhân, cho phép nhà phát triển tập trung vào việc thể hiện thuật toán thay vì lên lịch công việc. RenderScript đặc biệt hữu ích cho các ứng dụng xử lý hình ảnh, nhiếp ảnh điện toán hoặc thị giác máy tính.

Các thiết bị chạy Android 8.0 trở lên sử dụng khung RenderScript và HAL của nhà cung cấp sau đây:

Hình 1. Mã nhà cung cấp liên kết đến các thư viện nội bộ.

Những điểm khác biệt so với RenderScript trong Android 7.x trở xuống bao gồm:

  • Hai phiên bản của các thư viện nội bộ RenderScript trong một quy trình. Một nhóm dành cho đường dẫn dự phòng của CPU và nằm ngay tại /system/lib; nhóm còn lại dành cho đường dẫn GPU và nằm tại /system/lib/vndk-sp.
  • Các thư viện nội bộ RS trong /system/lib được xây dựng trong nền tảng và được cập nhật khi system.img được nâng cấp. Tuy nhiên, các thư viện trong /system/lib/vndk-sp được tạo cho nhà cung cấp và không được cập nhật khi system.img được nâng cấp (mặc dù có thể được cập nhật để khắc phục vấn đề bảo mật, nhưng ABI của các thư viện này vẫn giữ nguyên).
  • Mã nhà cung cấp (RS HAL, trình điều khiển RS và bcc plugin) được liên kết với các thư viện nội bộ RenderScript nằm tại /system/lib/vndk-sp. Chúng không thể liên kết với các thư viện trong /system/lib vì các thư viện trong thư mục đó được tạo cho nền tảng và do đó có thể không tương thích với mã nhà cung cấp (tức là các biểu tượng có thể bị xoá). Làm như vậy sẽ khiến OTA chỉ có khung không thể thực hiện được.

Thiết kế

Các phần sau đây trình bày chi tiết thiết kế RenderScript trong Android 8.0 trở lên.

Các thư viện RenderScript dành cho nhà cung cấp

Phần này liệt kê các thư viện RenderScript (được gọi là Vendor NDK cho các HAL cùng quy trình hoặc VNDK-SP) có sẵn cho mã nhà cung cấp và có thể được liên kết. Phần này cũng trình bày chi tiết các thư viện bổ sung không liên quan đến RenderScript nhưng cũng được cung cấp cho mã nhà cung cấp.

Mặc dù danh sách thư viện sau đây có thể khác nhau giữa các bản phát hành Android, nhưng danh sách này là bất biến đối với một bản phát hành Android cụ thể; để xem danh sách mới nhất về các thư viện có sẵn, hãy tham khảo /system/etc/ld.config.txt.

RenderScript Libs Thư viện không phải RenderScript
  • android.hardware.graphics.renderscript@1.0.so
  • libRS_internal.so
  • libRSCpuRef.so
  • libblas.so
  • libbcinfo.so
  • libcompiler_rt.so
  • libRSDriver.so
  • libc.so
  • libm.so
  • libdl.so
  • libstdc++.so
  • liblog.so
  • libnativewindow.so
  • libsync.so
  • libvndksupport.so
  • libbase.so
  • libc++.so
  • libcutils.so
  • libutils.so
  • libhardware.so
  • libhidlbase.so
  • libhidltransport.so
  • libhwbinder.so
  • liblzma.so
  • libz.so
  • libEGL.so
  • libGLESv1_CM.so
  • libGLESv2.so

Cấu hình không gian tên của trình liên kết

Hạn chế liên kết ngăn mã nhà cung cấp sử dụng các thư viện không có trong VNDK-SP được thực thi trong thời gian chạy bằng cách sử dụng không gian tên trình liên kết. (Để biết thông tin chi tiết, hãy tham khảo bản trình bày Thiết kế VNDK.)

Trên thiết bị chạy Android 8.0 trở lên, tất cả HAL trong cùng quy trình (SP-HAL) ngoại trừ RenderScript đều được tải trong không gian tên trình liên kết sphal. RenderScript được tải vào không gian tên rs dành riêng cho RenderScript, một vị trí cho phép thực thi lỏng lẻo hơn một chút đối với các thư viện RenderScript. Vì quá trình triển khai RS cần tải mã bit đã biên dịch, nên /data/*/*.so được thêm vào đường dẫn của không gian tên rs (các SP-HAL khác không được phép tải các thư viện từ phân vùng dữ liệu).

Ngoài ra, không gian tên rs cho phép nhiều thư viện hơn so với các không gian tên khác. libmediandk.solibft2.so được hiển thị cho không gian tên rslibRS_internal.so có một phần phụ thuộc nội bộ vào các thư viện này.

Hình 2. Cấu hình không gian tên cho trình liên kết.

Trình điều khiển tải

Đường dẫn dự phòng của CPU

Tuỳ thuộc vào sự tồn tại của bit RS_CONTEXT_LOW_LATENCY khi tạo một ngữ cảnh RS, đường dẫn CPU hoặc GPU sẽ được chọn. Khi đường dẫn CPU được chọn, libRS_internal.so (chế độ triển khai chính của khung RS) sẽ được dlopen trực tiếp từ không gian tên trình liên kết mặc định, nơi cung cấp phiên bản nền tảng của các thư viện RS.

Hoàn toàn không sử dụng việc triển khai RS HAL của nhà cung cấp khi sử dụng đường dẫn dự phòng CPU và một đối tượng RsContext được tạo bằng mVendorDriverName rỗng. libRSDriver.so được dlopen (theo mặc định) và thư viện trình điều khiển được tải từ không gian tên default vì phương thức gọi (libRS_internal.so) cũng được tải trong không gian tên default.

Hình 3. Đường dẫn dự phòng của CPU.

Đường dẫn GPU

Đối với đường dẫn GPU, libRS_internal.so được tải theo cách khác. Trước tiên, libRS.so dùng android.hardware.renderscript@1.0.so (và libhidltransport.so cơ bản của nó) để tải android.hardware.renderscript@1.0-impl.so (một cách triển khai RS HAL của nhà cung cấp) vào một không gian tên trình liên kết khác có tên là sphal. Sau đó, HAL RS sẽ dlopen libRS_internal.so trong một không gian tên trình liên kết khác có tên là rs.

Các nhà cung cấp có thể cung cấp trình điều khiển RS của riêng họ bằng cách đặt cờ thời gian xây dựng OVERRIDE_RS_DRIVER, được nhúng vào quá trình triển khai RS HAL (hardware/interfaces/renderscript/1.0/default/Context.cpp). Sau đó, tên trình điều khiển này sẽ được dlopen cho ngữ cảnh RS cho đường dẫn GPU.

Việc tạo đối tượng RsContext được uỷ quyền cho việc triển khai RS HAL. HAL gọi lại khung RS bằng hàm rsContextCreateVendor() có tên của trình điều khiển để dùng làm đối số. Sau đó, khung RS sẽ tải trình điều khiển đã chỉ định khi RsContext được khởi tạo. Trong trường hợp này, thư viện trình điều khiển được tải vào vùng chứa tên rs vì đối tượng RsContext được tạo bên trong vùng chứa tên rs/vendor/lib nằm trong đường dẫn tìm kiếm của vùng chứa tên.

Hình 4. Đường dẫn dự phòng của GPU.

Khi chuyển đổi từ không gian tên default sang không gian tên sphal, libhidltransport.so sẽ dùng hàm android_load_sphal_library() để sắp xếp rõ ràng trình liên kết động nhằm tải thư viện -impl.so từ không gian tên sphal.

Khi chuyển đổi từ không gian tên sphal sang không gian tên rs, quá trình tải được thực hiện gián tiếp bằng dòng sau trong /system/etc/ld.config.txt:

namespace.sphal.link.rs.shared_libs = libRS_internal.so

Dòng này chỉ định trình liên kết động sẽ tải libRS_internal.so từ không gian tên rs khi không tìm thấy/không tải được lib từ không gian tên sphal (trường hợp này luôn xảy ra vì không gian tên sphal không tìm kiếm /system/lib/vndk-sp nơi libRS_internal.so cư trú). Với cấu hình này, bạn chỉ cần gọi dlopen() đơn giản đến libRS_internal.so là đủ để thực hiện quá trình chuyển đổi không gian tên.

Tải trình bổ trợ bcc

bcc plugin là một thư viện do nhà cung cấp cung cấp, được tải vào trình biên dịch bcc. Vì bcc là một quy trình hệ thống trong thư mục /system/bin, nên thư viện bcc plugin có thể được coi là SP-HAL (tức là một HAL của nhà cung cấp có thể được tải trực tiếp vào quy trình hệ thống mà không cần được liên kết). Là một SP-HAL, thư viện bcc-plugin:

  • Không thể liên kết với các thư viện chỉ dành cho khung như libLLVM.so.
  • Chỉ có thể liên kết với các thư viện VNDK-SP mà nhà cung cấp có thể sử dụng.

Hạn chế này được thực thi bằng cách tải bcc plugin vào không gian tên sphal bằng hàm android_sphal_load_library(). Trong các phiên bản trước của Android, tên trình bổ trợ được chỉ định bằng cách sử dụng lựa chọn -load và lib được tải bằng cách sử dụng dlopen() đơn giản theo libLLVM.so. Trong Android 8.0 trở lên, điều này được chỉ định trong lựa chọn -plugin và lib được bcc tự động tải trực tiếp. Tuỳ chọn này cho phép một đường dẫn không dành riêng cho Android đến dự án LLVM nguồn mở.

Hình 5. Đang tải trình bổ trợ bcc, Android 7.x trở xuống.



Hình 6. Đang tải trình bổ trợ bcc, Android 8.0 trở lên.

Đường dẫn tìm kiếm cho ld.mc

Khi thực thi ld.mc, một số thư viện thời gian chạy RS được cung cấp dưới dạng đầu vào cho trình liên kết. Mã bit RS từ ứng dụng được liên kết với các thư viện thời gian chạy và khi mã bit đã chuyển đổi được tải vào một quy trình ứng dụng, các thư viện thời gian chạy sẽ được liên kết lại một cách linh động từ mã bit đã chuyển đổi.

Các thư viện thời gian chạy bao gồm:

  • libcompiler_rt.so
  • libm.so
  • libc.so
  • Trình điều khiển RS (libRSDriver.so hoặc OVERRIDE_RS_DRIVER)

Khi tải mã đối tượng đã biên dịch vào quy trình ứng dụng, hãy cung cấp chính xác thư viện mà ld.mc đã dùng. Nếu không, bitcode đã biên dịch có thể không tìm thấy một biểu tượng có sẵn khi được liên kết.

Để làm như vậy, khung RS sử dụng các đường dẫn tìm kiếm khác nhau cho các thư viện thời gian chạy khi thực thi ld.mc, tuỳ thuộc vào việc chính khung RS được tải từ /system/lib hay từ /system/lib/vndk-sp. Bạn có thể xác định điều này bằng cách đọc địa chỉ của một biểu tượng tuỳ ý của thư viện khung RS và sử dụng dladdr() để lấy đường dẫn tệp được liên kết với địa chỉ.

Chính sách SELinux

Do các thay đổi về chính sách SELinux trong Android 8.0 trở lên, bạn phải tuân theo các quy tắc cụ thể (được thực thi thông qua neverallows) khi gắn nhãn các tệp bổ sung trong phân vùng vendor:

  • vendor_file phải là nhãn mặc định cho tất cả các tệp trong phân vùng vendor. Chính sách nền tảng yêu cầu điều này để truy cập vào các chế độ triển khai HAL truyền qua.
  • Tất cả exec_types mới được thêm vào phân vùng vendor thông qua SEPolicy của nhà cung cấp đều phải có thuộc tính vendor_file_type. Việc này được thực thi thông qua neverallows.
  • Để tránh xung đột với các bản cập nhật nền tảng/khung trong tương lai, hãy tránh gắn nhãn cho các tệp khác ngoài exec_types trong phân vùng vendor.
  • Tất cả các phần phụ thuộc thư viện cho các HAL cùng quy trình do AOSP xác định đều phải được gắn nhãn là same_process_hal_file.

Để biết thông tin chi tiết về chính sách SELinux, hãy xem bài viết Security-Enhanced Linux trong Android.

Khả năng tương thích ABI cho mã bit

Nếu không có API mới nào được thêm, tức là không có sự gia tăng phiên bản HAL, thì các khung RS sẽ tiếp tục sử dụng trình điều khiển GPU hiện có (HAL 1.0).

Đối với các thay đổi nhỏ về HAL (HAL 1.1) không ảnh hưởng đến mã đối tượng, các khung sẽ dự phòng cho CPU đối với những API mới được thêm này và tiếp tục sử dụng trình điều khiển GPU (HAL 1.0) ở nơi khác.

Đối với các thay đổi lớn về HAL (HAL 2.0) ảnh hưởng đến quá trình biên dịch/liên kết mã đối tượng, các khung RS không nên tải trình điều khiển GPU do nhà cung cấp cung cấp mà thay vào đó, hãy sử dụng đường dẫn CPU hoặc Vulkan để tăng tốc.

Việc sử dụng mã bit RenderScript diễn ra theo 3 giai đoạn:

Giai đoạn Chi tiết
Biên dịch
  • Mã bit đầu vào (.bc) cho bcc phải ở định dạng mã bit LLVM 3.2bcc phải tương thích ngược với các ứng dụng hiện có (cũ).
  • Tuy nhiên, siêu dữ liệu trong .bc có thể thay đổi (có thể có các hàm thời gian chạy mới, ví dụ: Các hàm setter và getter phân bổ, hàm toán học, v.v.). Một phần của các hàm thời gian chạy nằm trong libclcore.bc, một phần nằm trong LibRSDriver hoặc tương đương với nhà cung cấp.
  • Các hàm thời gian chạy mới hoặc các thay đổi quan trọng về siêu dữ liệu yêu cầu tăng cấp độ API bitcode. Vì trình điều khiển của nhà cung cấp sẽ không thể sử dụng phiên bản này, nên bạn cũng phải tăng phiên bản HAL.
  • Các nhà cung cấp có thể có trình biên dịch riêng, nhưng các kết luận/yêu cầu đối với bcc cũng áp dụng cho những trình biên dịch đó.
Đường liên kết
  • .o đã biên dịch sẽ được liên kết với trình điều khiển của nhà cung cấp, ví dụ: libRSDriver_foo.solibcompiler_rt.so. Đường dẫn CPU sẽ liên kết với libRSDriver.so.
  • Nếu .o yêu cầu một API thời gian chạy mới từ libRSDriver_foo, thì trình điều khiển của nhà cung cấp phải được cập nhật để hỗ trợ API đó.
  • Một số nhà cung cấp có thể có trình liên kết riêng, nhưng đối số cho ld.mc cũng áp dụng cho họ.
Tải
  • libRSCpuRef tải đối tượng dùng chung. Nếu có thay đổi đối với giao diện này, bạn cần tăng phiên bản HAL.
  • Các nhà cung cấp sẽ dựa vào libRSCpuRef để tải đối tượng được chia sẻ hoặc triển khai đối tượng của riêng họ.

Ngoài HAL, các API thời gian chạy và các biểu tượng đã xuất cũng là các giao diện. Cả hai giao diện này đều không thay đổi kể từ Android 7.0 (API 24) và hiện không có kế hoạch thay đổi trong Android 8.0 trở lên. Tuy nhiên, nếu giao diện thay đổi, phiên bản HAL cũng sẽ tăng lên.

Triển khai nhà cung cấp

Android 8.0 trở lên yêu cầu một số thay đổi đối với trình điều khiển GPU để trình điều khiển GPU hoạt động đúng cách.

Mô-đun trình điều khiển

  • Các mô-đun trình điều khiển không được phụ thuộc vào bất kỳ thư viện hệ thống nào không có trong danh sách.
  • Trình điều khiển phải cung cấp android.hardware.renderscript@1.0-impl_{NAME} riêng hoặc khai báo chế độ triển khai mặc định android.hardware.renderscript@1.0-impl làm phần phụ thuộc.
  • Việc triển khai CPU libRSDriver.so là một ví dụ điển hình về cách xoá các phần phụ thuộc không phải VNDK-SP.

Trình biên dịch mã bit

Bạn có thể biên dịch mã bit RenderScript cho trình điều khiển của nhà cung cấp theo hai cách:

  1. Gọi trình biên dịch RenderScript dành riêng cho nhà cung cấp trong /vendor/bin/ (phương thức biên dịch GPU được ưu tiên). Tương tự như các mô-đun trình điều khiển khác, tệp nhị phân trình biên dịch của nhà cung cấp không thể phụ thuộc vào bất kỳ thư viện hệ thống nào không có trong danh sách các thư viện RenderScript mà nhà cung cấp có thể sử dụng.
  2. Gọi bcc hệ thống: /system/bin/bcc bằng bcc plugin do nhà cung cấp cung cấp; trình bổ trợ này không thể phụ thuộc vào bất kỳ thư viện hệ thống nào không có trong danh sách các thư viện RenderScript mà nhà cung cấp có thể sử dụng.

Nếu nhà cung cấp bcc plugin cần can thiệp vào quá trình biên dịch CPU và không thể dễ dàng xoá phần phụ thuộc của quá trình này trên libLLVM.so, thì nhà cung cấp nên sao chép bcc (và tất cả các phần phụ thuộc không phải LL-NDK, bao gồm cả libLLVM.so, libbcc.so) vào phân vùng /vendor.

Ngoài ra, nhà cung cấp cần thực hiện những thay đổi sau:

Hình 7. Thay đổi đối với trình điều khiển của nhà cung cấp.

  1. Sao chép libclcore.bc vào phân vùng /vendor. Điều này đảm bảo libclcore.bc, libLLVM.solibbcc.so được đồng bộ hoá.
  2. Thay đổi đường dẫn đến tệp thực thi bcc bằng cách thiết lập RsdCpuScriptImpl::BCC_EXE_PATH từ quá trình triển khai RS HAL.

Chính sách SELinux

Chính sách SELinux ảnh hưởng đến cả trình điều khiển và các tệp thực thi của trình biên dịch. Tất cả các mô-đun trình điều khiển phải được gắn nhãn same_process_hal_file trong file_contexts của thiết bị. Ví dụ:

/vendor/lib(64)?/libRSDriver_EXAMPLE\.so     u:object_r:same_process_hal_file:s0

Quy trình của ứng dụng phải có khả năng gọi tệp thực thi của trình biên dịch, cũng như bản sao của nhà cung cấp bcc (/vendor/bin/bcc). Ví dụ:

device/vendor_foo/device_bar/sepolicy/file_contexts:
/vendor/bin/bcc                    u:object_r:same_process_hal_file:s0

Thiết bị cũ

Thiết bị cũ là những thiết bị đáp ứng các điều kiện sau:

  1. PRODUCT_SHIPPING_API_LEVEL thấp hơn 26.
  2. PRODUCT_FULL_TREBLE_OVERRIDE chưa được xác định.

Đối với các thiết bị cũ, các hạn chế sẽ không được thực thi khi nâng cấp lên Android 8.0 trở lên, tức là các trình điều khiển có thể tiếp tục liên kết với các thư viện trong /system/lib[64]. Tuy nhiên, do thay đổi về cấu trúc liên quan đến OVERRIDE_RS_DRIVER, nên android.hardware.renderscript@1.0-impl phải được cài đặt vào phân vùng /vendor; nếu không, thời gian chạy RenderScript sẽ buộc phải quay lại đường dẫn CPU.

Để biết thông tin về lý do khiến Renderscript không được dùng nữa, hãy xem bài đăng trên Blog dành cho nhà phát triển Android: Android GPU Compute Going Forward (Điện toán GPU trên Android trong tương lai). Thông tin về tài nguyên cho việc ngừng sử dụng này bao gồm những nội dung sau: