• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# NativeDisplaySoloist开发指导 (C/C++)
2
3如果开发者想在独立线程中进行帧率控制的Native侧业务,可以通过DisplaySoloist来实现,如游戏、自绘制UI框架对接等场景。
4开发者可以选择多个DisplaySoloist实例共享一个线程,也可以选择每个DisplaySoloist实例独占一个线程。
5
6## 接口说明
7
8| 函数名称                                                     | 说明                                                  |
9| ------------------------------------------------------------ | ----------------------------------------------------- |
10| OH_DisplaySoloist* OH_DisplaySoloist_Create (bool useExclusiveThread) | 创建一个OH_DisplaySoloist实例。                       |
11| OH_DisplaySoloist_Destroy (OH_DisplaySoloist * displaySoloist) | 销毁一个OH_DisplaySoloist实例。                       |
12| OH_DisplaySoloist_Start (OH_DisplaySoloist * displaySoloist, OH_DisplaySoloist_FrameCallback callback, void * data ) | 设置每帧回调函数,每次VSync信号到来时启动每帧回调。   |
13| OH_DisplaySoloist_Stop (OH_DisplaySoloist * displaySoloist)  | 停止请求下一次VSync信号,并停止调用回调函数callback。 |
14| OH_DisplaySoloist_SetExpectedFrameRateRange (OH_DisplaySoloist* displaySoloist, DisplaySoloist_ExpectedRateRange* range) | 设置期望帧率范围。                                    |
15
16## 开发示例
17
18   本范例是通过Drawing在Native侧实现图形的绘制,通过异步线程设置期望的帧率,再根据帧率进行图形的绘制并将其呈现在NativeWindow上,图形绘制部分可参考[使用Drawing实现图形绘制与显示](graphic-drawing-overview.md)。
19
20### 添加开发依赖
21
22**添加动态链接库**
23
24CMakeLists.txt中添加以下lib。
25
26```txt
27libace_napi.z.so
28libace_ndk.z.so
29libnative_window.so
30libnative_drawing.so
31libnative_display_soloist.so
32```
33
34**头文件**
35
36```c++
37#include <ace/xcomponent/native_interface_xcomponent.h>
38#include "napi/native_api.h"
39#include <native_display_soloist/native_display_soloist.h>
40#include <native_drawing/drawing_bitmap.h>
41#include <native_drawing/drawing_color.h>
42#include <native_drawing/drawing_canvas.h>
43#include <native_drawing/drawing_pen.h>
44#include <native_drawing/drawing_brush.h>
45#include <native_drawing/drawing_path.h>
46#include <native_window/external_window.h>
47#include <cmath>
48#include <algorithm>
49#include <stdint.h>
50#include <sys/mman.h>
51```
52
53### 开发步骤
54
551. 定义ArkTS接口文件XComponentContext.ts,用来对接Native层。
56   ```ts
57   export default interface XComponentContext {
58     register(): void;
59     unregister(): void;
60     destroy(): void;
61   };
62   ```
63
642. 定义演示页面,包含两个XComponent组件。
65
66   ```ts
67   import XComponentContext from "../interface/XComponentContext";
68
69   @Entry
70   @Component
71   struct Index {
72     private xComponentContext1: XComponentContext | undefined = undefined;
73     private xComponentContext2: XComponentContext | undefined = undefined;
74
75     build() {
76       Column() {
77         Row() {
78           XComponent({ id: 'xcomponentId30', type: 'surface', libraryname: 'entry' })
79             .onLoad((xComponentContext) => {
80               this.xComponentContext1 = xComponentContext as XComponentContext;
81             }).width('640px')
82         }.height('40%')
83
84         Row() {
85           XComponent({ id: 'xcomponentId120', type: 'surface', libraryname: 'entry' })
86             .onLoad((xComponentContext) => {
87               this.xComponentContext2 = xComponentContext as XComponentContext;
88             }).width('640px') // 64的倍数
89         }.height('40%')
90       }
91     }
92   }
93   ```
94
953. 在 Native C++层获取NativeXComponent。建议使用单例模式保存XComponent。此步骤需要在napi_init的过程中处理。
96
97    创建一个PluginManger单例类,用于管理NativeXComponent。
98    ```c++
99    class PluginManager {
100    public:
101        ~PluginManager();
102
103        static PluginManager *GetInstance();
104
105        void SetNativeXComponent(std::string &id, OH_NativeXComponent *nativeXComponent);
106        SampleBitMap *GetRender(std::string &id);
107        void Export(napi_env env, napi_value exports);
108    private:
109
110        std::unordered_map<std::string, OH_NativeXComponent *> nativeXComponentMap_;
111        std::unordered_map<std::string, SampleXComponent *> pluginRenderMap_;
112    };
113    ```
114    SampleXComponent类会在后面的绘制图形中创建。
115    ```c++
116    void PluginManager::Export(napi_env env, napi_value exports) {
117        nativeXComponentMap_.clear();
118        pluginRenderMap_.clear();
119        if ((env == nullptr) || (exports == nullptr)) {
120            DRAWING_LOGE("Export: env or exports is null");
121            return;
122        }
123
124        napi_value exportInstance = nullptr;
125        if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
126            DRAWING_LOGE("Export: napi_get_named_property fail");
127            return;
128        }
129
130        OH_NativeXComponent *nativeXComponent = nullptr;
131        if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&nativeXComponent)) != napi_ok) {
132            DRAWING_LOGE("Export: napi_unwrap fail");
133            return;
134        }
135
136        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
137        uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
138        if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
139            DRAWING_LOGE("Export: OH_NativeXComponent_GetXComponentId fail");
140            return;
141        }
142
143        std::string id(idStr);
144        auto context = PluginManager::GetInstance();
145        if ((context != nullptr) && (nativeXComponent != nullptr)) {
146            context->SetNativeXComponent(id, nativeXComponent);
147            auto render = context->GetRender(id);
148            if (render != nullptr) {
149                render->RegisterCallback(nativeXComponent);
150                render->Export(env, exports);
151            } else {
152                DRAWING_LOGE("render is nullptr");
153            }
154        }
155    }
156    ```
157
1584. Native层配置帧率和注册回调函数。
159
160   定义每帧回调函数内容。
161
162   ```c++
163   static void TestCallback(long long timestamp, long long targetTimestamp, void *data)
164   {
165      // ...
166      // 获取对应的XComponent
167       OH_NativeXComponent *component = nullptr;
168       component = static_cast<OH_NativeXComponent *>(data);
169       if (component == nullptr) {
170          SAMPLE_LOGE("TestCallback: component is null");
171          return;
172       }
173       char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
174       uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
175       if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
176          SAMPLE_LOGE("TestCallback: Unable to get XComponent id");
177          return;
178       }
179
180       std::string id(idStr);
181       auto render = SampleXComponent::GetInstance(id);
182       OHNativeWindow *nativeWindow = render->GetNativeWindow();
183       uint64_t width;
184       uint64_t height;
185       // 获取XComponent的surface大小
186       int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, nativeWindow, &width, &height);
187       if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) {
188           render->Prepare();
189           render->Create();
190           if (id == "xcomponentId30") {
191               // 30Hz绘制时,每帧移动的距离为16像素
192               render->ConstructPath(16, 16, render->defaultOffsetY);
193           }
194           if (id == "xcomponentId120") {
195               // 120Hz绘制时,每帧移动的距离为4像素
196               render->ConstructPath(4, 4, render->defaultOffsetY);
197           }
198     	 // ...
199       }
200   }
201   ```
202
203   使用DisplaySoloist接口配置帧率和注册每帧回调函数。
204
205   > **说明:**
206   >
207   > - 实例在调用NapiRegister后,在不需要进行帧率控制时,应进行NapiUnregister操作,避免内存泄漏问题。
208   > - 在页面跳转时,应进行NapiUnregister和NapiDestroy操作,避免内存泄漏问题。
209
210   ```c++
211   static std::unordered_map<std::string, OH_DisplaySoloist *> g_displaySync;
212
213   napi_value SampleXComponent::NapiRegister(napi_env env, napi_callback_info info)
214   {
215       // ...
216       // 获取对应的XComponent
217       napi_value thisArg;
218       if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) {
219          SAMPLE_LOGE("NapiRegister: napi_get_cb_info fail");
220          return nullptr;
221       }
222
223       napi_value exportInstance;
224       if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
225          SAMPLE_LOGE("NapiRegister: napi_get_named_property fail");
226          return nullptr;
227       }
228
229       OH_NativeXComponent *nativeXComponent = nullptr;
230       if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&nativeXComponent)) != napi_ok) {
231          SAMPLE_LOGE("NapiRegister: napi_unwrap fail");
232          return nullptr;
233       }
234
235       char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
236       uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
237       if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
238          SAMPLE_LOGE("NapiRegister: Unable to get XComponent id");
239          return nullptr;
240       }
241       SAMPLE_LOGI("RegisterID = %{public}s", idStr);
242       std::string id(idStr);
243       SampleXComponent *render = SampleXComponent().GetInstance(id);
244       if (render != nullptr) {
245          OH_DisplaySoloist *nativeDisplaySoloist = nullptr;
246          if (g_displaySync.find(id) == g_displaySync.end()) {
247             // 创建OH_DisplaySoloist实例
248             // true表示OH_DisplaySoloist实例独占一个线程,false则表示共享一个线程
249             g_displaySync[id] = OH_DisplaySoloist_Create(true);
250          }
251          nativeDisplaySoloist = g_displaySync[id];
252          // 设置期望帧率范围
253          // 此结构体成员变量分别为帧率范围的最小值、最大值以及期望帧率
254          DisplaySoloist_ExpectedRateRange range;
255          if (id == "xcomponentId30") {
256             // 第一个XComponent期望帧率为30Hz
257             range = {30, 120, 30};
258          }
259          if (id == "xcomponentId120") {
260             // 第二个XComponent期望帧率为120Hz
261             range = {30, 120, 120};
262          }
263          OH_DisplaySoloist_SetExpectedFrameRateRange(nativeDisplaySoloist, &range);
264          // 注册回调与使能每帧回调
265          OH_DisplaySoloist_Start(nativeDisplaySoloist, TestCallback, nativeXComponent);
266       }
267       // ...
268   }
269
270   napi_value SampleXComponent::NapiUnregister(napi_env env, napi_callback_info info)
271   {
272       // ...
273       // 取消注册每帧回调
274       OH_DisplaySoloist_Stop(g_displaySync[id]);;
275       // ...
276   }
277
278   napi_value SampleXComponent::NapiDestroy(napi_env env, napi_callback_info info)
279   {
280       // ...
281       // 销毁OH_DisplaySoloist实例
282       OH_DisplaySoloist_Destroy(g_displaySync[id]);
283       g_displaySync.erase(id);
284       // ...
285   }
286
287   // 实现XComponentContext.ts中ArkTS接口与C++接口的绑定和映射。
288   void SampleXComponent::Export(napi_env env, napi_value exports) {
289    if ((env == nullptr) || (exports == nullptr)) {
290        SAMPLE_LOGE("Export: env or exports is null");
291        return;
292    }
293    napi_property_descriptor desc[] = {
294        {"register", nullptr, SampleXComponent::NapiRegister, nullptr, nullptr, nullptr, napi_default, nullptr},
295        {"unregister", nullptr, SampleXComponent::NapiUnregister, nullptr, nullptr, nullptr, napi_default, nullptr},
296        {"destroy", nullptr, SampleXComponent::NapiDestroy, nullptr, nullptr, nullptr, napi_default, nullptr}};
297
298    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
299    if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
300        SAMPLE_LOGE("Export: napi_define_properties failed");
301    }
302   }
303   ```
304
3055. TS层注册和取消注册每帧回调,销毁OH_DisplaySoloist实例。
306
307   ```c++
308   // 离开页面时,取消回调注册与销毁OH_DisplaySoloist实例
309   aboutToDisappear(): void {
310     if (this.xComponentContext1) {
311       this.xComponentContext1.unregister();
312       this.xComponentContext1.destroy();
313     }
314     if (this.xComponentContext2) {
315       this.xComponentContext2.unregister();
316       this.xComponentContext2.destroy();
317     }
318   }
319
320   Row() {
321       Button('Start')
322         .id('Start')
323         .fontSize(14)
324         .fontWeight(500)
325         .margin({ bottom: 20, right: 6, left: 6 })
326         .onClick(() => {
327           if (this.xComponentContext1) {
328             this.xComponentContext1.register();
329           }
330           if (this.xComponentContext2) {
331             this.xComponentContext2.register();
332           }
333         })
334         .width('30%')
335         .height(40)
336         .shadow(ShadowStyle.OUTER_DEFAULT_LG)
337
338       Button('Stop')
339         .id('Stop')
340         .fontSize(14)
341         .fontWeight(500)
342         .margin({ bottom: 20, left: 6 })
343         .onClick(() => {
344           if (this.xComponentContext1) {
345             this.xComponentContext1.unregister();
346           }
347           if (this.xComponentContext2) {
348             this.xComponentContext2.unregister();
349           }
350         })
351         .width('30%')
352         .height(40)
353         .shadow(ShadowStyle.OUTER_DEFAULT_LG)
354   }
355   ```
356
357## 相关实例
358
359针对可变帧率的开发,有以下相关实例可供参考:
360
361- [DisplaySoloist分级管控(API12)(C/C++)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Graphics/DisplaySoloist)