Lưu ý: Bài viết này dựa vào sách OpenGL ES 2.0 programming guide kết hợp với các ví dụ mới được code lại, các thuật ngữ cũng được lược giản lại để dễ đọc và hiểu hơn

Cú pháp EGL

Trước khi nói về cú pháp, chúng ta tìm hiểu cùng tìm hiểu về EGL. EGL là một lớp nằm giữa dùng để giao tiếp hàm đồ họa Khronos GLES 2.0 với window system (cửa sổ hệ thống).

EGL được thiết lập trước khi GLES khởi chạy trong các trường hợp như:

  • Thiết bị có nhiều màn hình, nó sẽ tìm xem màn hình nào có sẵn trên thiết bị, lúc này GLES sẽ được khởi tạo.
  • Khởi tạo rendering surface (bề mặt đồ họa) với hai loại là off-screen surface (bề mặt không hiển thị) và on-screen surface (bề mặt hiển thị). On-screen surface được dùng để kết nối với window system để hiển thị, trong khi đó off-screen surface được dùng cho các ứng dụng xử lý không cần hiển thị trên màn hình.
  • Khởi tạo rendering context (trình ngữ cảnh đồ họa) với render surface phù hợp để GLES bắt đầu render.

Samsung Galaxy Fold chạy Google Android có hai màn hình chơi game Gameloft Asphalt 9 sử dụng Khronos GLES 3.0 với EGL (nguồn hình ảnh: Booredatwork.com).

Lưu ý: hình ảnh chụp vào năm 2019 - khi đó Asphalt 9 vẫn đang dùng GLES 3.0

EGL version mới nhất là 1.5 phát hành năm 2015

Cú pháp hàm EGL bắt đầu với tiền tố egl với các chữ cái đầu của lệnh viết hoa chữ cái đầu mỗi chữ - camel case (ví dụ: eglCreateWindowSurface). Cú pháp dữ liệu EGL sẽ bắt đầu bằng chữ viết hoa tiền tố EGL và viết hoa các chữ cái đầu tiên (trường hợp ngoại lệ gồm EGLintEGLenum).

Bảng dữ liệu tương ứng EGL và ngôn ngữ C

Kiểu dữ liệuNgôn ngữ CEGL
32-bit integerintEGLint
32-bit unsigned integerunsigned intEGLBoolean, EGLenum
32-bit pointervoid*EGLConfig, EGLContext, EGLDisplay, EGLSurface, EGLClientBuffer

Cú pháp GLES

Cú pháp hàm GLES bắt đầu với tiền tố gl với các chữ cái đầu của lệnh viết hoa chữ cái đầu mỗi chữ - camel case (ví dụ: glBlendEquation). Cú pháp dữ liệu GLES sẽ bắt đầu bằng chữ viết hoa tiền tố GL và viết hoa các chữ cái đầu tiên.

Ngoài ra, chúng ta còn các tham số thể hiện sự khác nhau. Bao gồm số lượng thành phần đầu vào (một đến bốn tham số đầu vào), kiểu dữ liệu đầu vào (byte - b, unsigned byte - ub, short - s, unsigned short - us, int - i, fixed - x, float - f) hoặc giá trị là vector - v hay không.

Ví dụ:

glUniform2f(location, 1.0f, 0.0f);
glUniform2i(location, 1, 0);

Hai hàm trên là giống nhau, tuy nhiên chúng có sự khác biệt khi một cái nhận uniform là float và cái còn lại là int.

GLfloat coord[4] = { 1.0f, 0.75f, 0.25f, 0.0f };
glUniform4fv(location, coord);
glUniform4f(location, coord[0], coord[1], coord[2], coord[3]);

Hai hàm trên là giống nhau, tuy nhiên sự khác biệt nằm ở chỗ một cái nhận tham số đầu vào là vector cái còn lại thì không

Bảng dữ liệu tương ứng của GLES và ngôn ngữ C

Kiểu dữ liệuKiểu dữ liệuNgôn ngữ CGLES
b8-bit signed integersigned charGLbyte
ub8-bit unsigned integerunsigned charGLubyte, GLboolean
s16-bit signed integershortGLshort
us16-bit unsigned integerunsigned shortGLushort
i32-bit signed integerintGLint
ui32-bit unsigned integerunsigned intGLuint, GLbitfield, GLenum
x16.16 fixed pointintGLfixed
f32-bit floating pointfloatGLfloat, GLclampf 

GLES còn kiểu dữ liệu nữa là GLvoid dùng cho con trỏ.

Trong phần lớn các bài còn lại của mình, mình sẽ sử dụng tên hàm + dấu sao * cho việc thay thế các sự khác biệt. Chẳng hạn hàm glUniform* dùng cho cả các tham số khác nhau.

Xử lý lỗi

Các hàm GLES khi hoạt động có vấn đề sẽ tạo ra mã lỗi. Các lỗi này có thể xem bằng hàm glGetError. Khi hàm này bắt đầu chạy mã lỗi được tạo ra được gán giá trị mặc định là GL_NO_ERROR. Mã lỗi này sẽ bị bỏ qua và các hàm GLES tiếp tục chạy cho đến khi mã lỗi GL_OUT_OF_MEMORY xuất hiện.

GLenum glGetError(void)

Hàm này sẽ trả về mã lỗi hiện tại nó gặp sau đó sẽ được trả lại về mặc định là GL_NO_ERROR. Nếu kết quả trả lại là GL_NO_ERROR tức là không có lỗi xuất hiện.

Mã lỗiMô tả
GL_NO_ERRORKhông có lỗi nào từ khi glGetError được gọi lần cuối.
GL_INVALID_ENUMTham số GLenum truyền vào quá lớn. Lúc này hàm tạo mã lỗi sẽ bị bỏ qua.
GL_INVALID_VALUETham số số học truyền vào quá lớn. Lúc này hàm tạo mã lỗi sẽ bị bỏ qua.
GL_INVALID_OPERATIONHàm thực thi không thể thực thi trên trạng thái GLES. Lúc này hàm tạo mã lỗi sẽ bị bỏ qua.
GL_OUT_OF_MEMORYKhông đủ bộ nhớ để thực thi hàm. Trình tự xử lý trạng thái GLES sẽ xem xét không xác định nếu lỗi này không thể xác định

Dọn dẹp hàm chờ

GLES kế thừa mô hình render client-server của OpenGL. Trong OpenGL có thể render ở máy server sau đó kết quả sẽ được hiển thị trên client khác. Còn GLES thì máy chủ và máy con đều là một.

Với mô hình này, các lệnh gửi từ client lên server chưa chắc đã được xử lý ngay lập tức. Do đó các client sẽ lưu trữ lại thành các buffer và sẽ gửi cho server sau đó. Lúc này server phải xử lý các lệnh được gửi trước đó xong mới thực hiện lệnh tiếp theo do client gửi lên.

Khi GLES có nhiều ngữ cảnh khác nhau (chạy trên nhiều thread khác nhau) còn chia sẻ các đối tượng xử lý. Để đồng bộ các ngữ cảnh này, trong trường hợp B phụ thuộc A thì ngữ cảnh A phải được xử lý trước sau đó mới đến B. Hàm glFlush sẽ được dùng để loại các hàm đang được chờ trong ngữ cảnh hiện tại, và hàm này thực thi mà không cần chờ hàm đang xử lý thực hiện xong. Nếu cần chờ cho xử lý xong hiện tại rồi mới loại hàm được chờ trong ngữ cảnh thì hàm glFinish sẽ được dùng. Lưu ý không dùng hàm glFinish khi không thực sự cần thiết vì hàm này sẽ chờ cho đến khi hàm đang thực thi hoàn toàn thực thi xong rồi mới xử lý do đó hàm này sẽ ảnh hưởng đến hiệu năng của ứng dụng.

void glFlush(void)
void glFinish(void)

Quản lý trạng thái cơ bản

Các giai đoạn trong tiến trình xử lý GLES được nói ở phần 2 của bài. Với mỗi giai đoạn có thể xử lý bằng cách bật hoặc tắt cho phù hợp với từng giai đoạn ngữ cảnh. Ví dụ giai đoạn blending enable (trộn lẫn màu sắc fragment), cull enable (sắp xếp đỉnh của các đỉnh trong tam giác). Giai đoạn này sẽ được thực khởi tạo bằng ngữ cảnh mặc định sau khi EGLcontext thực hiện xong. Trạng thái của nó có thể được bật - glEnable hoặc tắt - glDisable.

void glDisable(GLenum cap)
void glEnable(GLenum cap)

glEnable và glDisable được dùng để bật tắt các thiết lập. Lúc khởi tạo tất các giá trị là GL_FALSE, nhưng trừ thiết lập GL_DITHER có giá trị là GL_TRUE. Mã lỗi là GL_INVALID_ENUM sẽ được tạo nếu cap là không phải là giá trị có hiệu lực.

Ngoài ra chúng ta có thể kiểm tra xem một thiết lập trạng thái hiện tại có bật lên hay không bằng hàm glIsEnabled

GLboolean glIsEnabled(GLenum cap)

Giá trị trả về sẽ là GL_TRUE hoặc là GL_FALSE khi thiết lập đó được kiểm tra. Mã lỗi là GL_INVALID_ENUM sẽ được tạo nếu cap là không phải là giá trị có hiệu lực.

Ngoài ra từng thiết lập một có thể được kiểm tra bằng hàm glGet*** với *** là các thiết lập khác nhau.

Comments


Comments are closed