DAYU200是一款支援OpenHarmony的富裝置,其硬體支援GPU,OpenHarmony的圖形架構也使能了GPU相關功能。DAYU200的GPU型号為Mali-G52,支援OpenGL ES 1.1/2.0/3.2,OpenCL 2.0,Vulkan 1.1。本文分享在DAYU200上,以OpenHarmony為平台,如何搭建OpenGL開發環境,及用最簡單的OpenGL API(C/C++語言)繪制基本圖形 —— 三角形。
本文能夠在OpenHarmony上采用OpenGL的API實作繪制基本圖形,參考了相關開源項目或文章,在此對相關作者表示感謝。
一、OpenHarmony圖形棧
OpenHarmony 圖形棧如下圖所示。OpenHarmony接口層,提供圖形的 Native API能力,包括:WebGL、Native Drawing的繪制能力、OpenGL指令級的繪制能力支撐等。
OpenHarmony應用開發是主要基于JS API,平時大家開發應用,基本用不到C/C++的方式。了解如何使用OpenHarmony的Native Drawing的繪制能力及OpenGL指令來繪制一些簡單圖形,對加深OpenHarmony圖形棧的認識頗有意義。
二、開發環境搭建
如果使用OpenGL API直接繪制基本圖形,需要依賴OpenHarmony的接口層的相關API,如何友善使用,主要參考Gitee項目(https://gitee.com/honglianglin/glmark2_2)。
1、NativeWindow
将native_window_wrapper源碼及BUILD.gn,加入到OpenHarmony編譯後,生成libnative_window_wrapper.z.so庫,OpenGL應用程式,會調用該動态庫進行視窗的建立等。
庫編譯好後,可以使用hdc_std指令,推送到開發闆端,參考指令如下:
hdc_std shell mount -o remount,rw /
hdc_std file send libnative_window_wrapper.z.so /system/lib
相關源碼見附件(native_window_wrapper.zip),其中native_window_wrapper.h頭檔案内容如下:
#ifndef NATIVE_WINDOW_WRAPPER
#define NATIVE_WINDOW_WRAPPER
#include <cstdint>
extern "C" {
typedef struct {
void* (*CreateWindowWrapper)();
bool (*CreateWindow)(void* wrapper, uint32_t w, uint32_t h);
void* (*GetNativeWindow)(void* wrapper);
void (*SetVisibility)(void* wrapper, bool visible);
void (*DestroyWindowWrapper)(void* wrapper);
} WrapperFunc;
bool GetWrapperFunc(WrapperFunc* funcs);
}
#endif // NATIVE_WINDOW_WRAPPER
2、應用開發
為了便于直接開發使用OpenGL API的程式,環境基于glmark2,删除了其中benchmark相關代碼,精簡為一個基于Make建構的工程(工程源碼見附件:native_window_ohos.zip),便于在OpenHarmony平台,應用開發快速驗證OpenGL相關API,目錄結構如下,開發時執行make即可編譯。
.
├── include
│ └── native_window_wrapper.h
├── main.cpp
├── Makefile
└── src
├── canvas-generic.cpp
├── canvas-generic.h
├── canvas.h
├── glad
├── gl-headers.cpp
├── gl-headers.h
├── gl-state-egl.cpp
├── gl-state-egl.h
├── gl-state.h
├── gl-visual-config.cpp
├── gl-visual-config.h
├── include
├── libmatrix
├── native-state.h
├── native-state-ohos.cpp
├── native-state-ohos.h
├── ohos_wrapper_linker.cpp
├── ohos_wrapper_linker.h
├── options.cpp
├── options.h
├── shared-library.cpp
└── shared-library.h
Makefile核心内容:
OHOS_ROOT = /home/algoideas/openharmony/master
CC = $(OHOS_ROOT)/prebuilts/clang/ohos/linux-x86_64/llvm/bin/clang --sysroot=$(OHOS_ROOT)/out/a311d/obj/third_party/musl
CXX = $(OHOS_ROOT)/prebuilts/clang/ohos/linux-x86_64/llvm/bin/clang++ --sysroot=$(OHOS_ROOT)/out/a311d/obj/third_party/musl
AR = $(OHOS_ROOT)/prebuilts/clang/ohos/linux-x86_64/llvm/bin/llvm-ar
LD = $(OHOS_ROOT)/prebuilts/clang/ohos/linux-x86_64/llvm/bin/llvm-link
ARFLAG = -rcs
TARGET = native_main
PROJECT_PATH = $(shell pwd)
CFLAGS := -march=armv7-a \
-mfloat-abi=softfp \
-mtune=generic-armv7-a \
-mfpu=neon \
-mthumb \
--target=arm-linux-ohosmusl \
--sysroot=$(OHOS_ROOT)/out/rk3568/obj/third_party/musl
# Warning
CFLAGS += -Wno-c++11-narrowing
# Lib
CLIBS = -lm -ldl -lrt
CLIBS += -L$(OHOS_ROOT)/device/soc/rockchip/hardware/gpu/lib -lmali-bifrost-g52-g2p0-ohos
CLIBS += -L$(OHOS_ROOT)/out/rk3568/packages/phone/system/lib -lhilog -lsurface.z -lutils.z
CFLAGS += -DOHOS_USE_DRM -DOHOS_USE_GLESv2 -DOHOS_USE_EGL
INCLUDE_DIRS += \
-I$(OHOS_ROOT)/third_party/EGL/api \
-I$(OHOS_ROOT)/third_party/openGLES/api \
-I$(OHOS_ROOT)/utils/native/base/include \
-I$(OHOS_ROOT)/drivers/peripheral/base \
-I$(OHOS_ROOT)/foundation/graphic/standard/interfaces/innerkits/common \
-I$(OHOS_ROOT)/foundation/graphic/standard/interfaces/innerkits/surface \
-I$(OHOS_ROOT)/foundation/graphic/standard/utils/buffer_handle/export \
-I$(OHOS_ROOT)/foundation/communication/ipc/interfaces/innerkits/ipc_core/include \
-I$(OHOS_ROOT)/foundation/aafwk/standard/frameworks/kits/ability/ability_runtime/include \
-I$(OHOS_ROOT)/foundation/aafwk/standard/interfaces/innerkits/ability_manager/include \
-I$(OHOS_ROOT)/foundation/aafwk/standard/interfaces/innerkits/app_manager/include/appmgr \
-I$(OHOS_ROOT)/third_party/jsoncpp/include \
-I$(OHOS_ROOT)/third_party/json/include \
-I$(OHOS_ROOT)/foundation/windowmanager/interfaces/innerkits/wm \
-I$(OHOS_ROOT)/foundation/graphic/standard/frameworks/surface \
-I$(OHOS_ROOT)/foundation/graphic/standard/rosen/modules/render_service_base/include \
-I$(OHOS_ROOT)/foundation/graphic/standard/rosen/modules/render_service_client/core \
-I$(OHOS_ROOT)/foundation/graphic/standard/rosen/modules/render_service_client \
-I$(OHOS_ROOT)/third_party/flutter/skia \
-I$(OHOS_ROOT)/third_party/flutter/skia/include/core \
-I$(OHOS_ROOT) \
-I$(OHOS_ROOT)/third_party/skia
INCLUDE_DIRS += -I$(PROJECT_PATH)/include \
-I$(PROJECT_PATH)/src/glad/include \
-I$(PROJECT_PATH)/src/libmatrix \
-I$(PROJECT_PATH)/src
export CC CXX CFLAGS AR LD ARFLAG MODULE_SELECT
CPP_SOURCES = $(wildcard ./src/*.cpp)
CPP_OBJECTS = $(patsubst %.cpp,%.o,$(CPP_SOURCES))
三、繪制基本圖形
采用OpenGL API,繪制基本圖形 - 三角形的源碼如下,繪制流程部分參考知乎。
#include "gl-headers.h"
#include "options.h"
#include "log.h"
#include "util.h"
#include "canvas-generic.h"
#include "native-state-ohos.h"
#include "gl-state-egl.h"
using std::vector;
using std::string;
int main(int argc, char *argv[])
{
if (!Options::parse_args(argc, argv))
return 1;
/* Initialize Log class */
Log::init(Util::appname_from_path(argv[0]), Options::show_debug);
if (Options::show_help) {
Options::print_help();
return 0;
}
/* Force 320x240 output for validation */
if (Options::validate &&
Options::size != std::pair<int,int>(320, 240))
{
Log::info("Ignoring custom size %dx%d for validation. Using 800x600.\n",
Options::size.first, Options::size.second);
Options::size = std::pair<int,int>(320, 240);
}
// Create the canvas
NativeStateOhos native_state;
GLStateEGL gl_state;
CanvasGeneric canvas(native_state, gl_state, Options::size.first, Options::size.second);
canvas.offscreen(Options::offscreen);
canvas.visual_config(Options::visual_config);
if (!canvas.init()) {
Log::error("%s: Could not initialize canvas\n", __FUNCTION__);
return 1;
}
Log::info("=======================================================\n");
canvas.print_info();
Log::info("=======================================================\n");
canvas.visible(true);
/**
** 資料處理: 生成和綁定VBO, 設定屬性指針
**/
// 三角形的頂點資料, 規範化(x,y,z)都要映射到[-1,1]之間
const float triangle[] = {
// 位置
-0.5f, -0.5f, 0.0f, // 左下
0.5f, -0.5f, 0.0f, // 右下
0.0f, 0.5f, 0.0f // 正上
};
// 生成并綁定立方體的VBO
GLuint vertex_buffer_object; // VBO
glGenBuffers(1, &vertex_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object);
// 将頂點資料綁定到目前預設的緩沖中, 好處是不用将頂點資料一個一個地發送到顯示卡上, 可以借助VBO一次性發送所有頂點資料
// GL_STATIC_DRAW表示頂點資料不會被改變
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle), triangle, GL_STATIC_DRAW);
// 設定頂點屬性指針
// 第一個參數0: 頂點着色器的位置值
// 第二個參數3: 位置屬性是一個三分量的向量
// 第三個參數: 頂點的類型
// 第四個參數: 是否希望資料标準化,映射到[0,1]
// 第五個參數: 步長,表示連續頂點屬性之間的間隔,下一組的資料再3個float之後
// 第六個參數: 資料的偏移量, 位置屬性在開頭, 是以為0, 還需要強制類型轉換
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0); // 開啟0通道
glBindBuffer(GL_ARRAY_BUFFER, 0);
/**
** 着色器: 頂點和片段着色器
**/
/* 着色器源碼 -> 生成并編譯着色器 -> 連結着色器到着色器程式 -> 删除着色器 */
const char *vertex_shader_source =
"attribute vec4 a_Position;\n" // 位置變量屬性設定為0
"void main()\n"
"{\n"
" gl_Position = a_Position;\n"
"}\n\0";
/* 設定片元像素的顔色為紅色, vec4(r,g,b,a) */
const char *fragment_shader_source =
"void main()\n"
"{\n"
" gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
"}\n\0";
/**
** 生成并編譯着色器
**/
/* 頂點着色器 */
int vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
glCompileShader(vertex_shader);
int success;
char info_log[512];
/* 檢查着色器是否成功編譯, 如果編譯失敗, 列印錯誤資訊 */
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
if(!success){
glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
Log::error("SHADER::VERTEX::COMPILATION_FAIILED %s\n", info_log);
}
/* 片元着色器 */
int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
glCompileShader(fragment_shader);
/* 檢查着色器是否成功編譯, 如果編譯失敗, 列印錯誤資訊 */
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
if(!success){
glGetShaderInfoLog(fragment_shader, 512, NULL, info_log);
Log::error("glGetShaderiv fragment_shader fail %s\n", info_log);
}
/* 連結頂點和片段着色器至一個着色器程式 */
int shader_program = glCreateProgram();
glAttachShader(shader_program, vertex_shader);
glAttachShader(shader_program, fragment_shader);
glLinkProgram(shader_program);
/* 檢查着色器是否成功連結, 如果連結失敗, 列印錯誤資訊 */
glGetProgramiv(shader_program, GL_LINK_STATUS, &success);
if(!success){
glGetProgramInfoLog(shader_program, 512, NULL, info_log);
Log::error("glGetShaderiv shader_program fail %s\n", info_log);
}
/* 删除頂點和片段着色器 */
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
/**
** 渲染
**/
/* 清空顔色緩沖 */
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // 用黑色背景色來清空
glClear(GL_COLOR_BUFFER_BIT);
/* 使用着色器程式 */
glUseProgram(shader_program);
/* 繪制三角形 */
glDrawArrays(GL_TRIANGLES, 0, 3); // 繪制三角形, 繪制三角形, 頂點起始索引值, 繪制數量
/* 更新交換緩沖 */
canvas.update();
glDeleteBuffers(1, &vertex_buffer_object);
getchar();
return 0;
}
四、運作效果展示
1、運作工程編譯出來的可執行程式native_main, 日志列印如下:
2、界面顯示效果如下(左上角區域 320x240):
附件連結:
native_window_ohos.zip(https://ost.51cto.com/resource/2014)
native_window_wrapper.zip(https://ost.51cto.com/resource/2012)