1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "NativeImageAdaptor.h"
17 #include "logger_common.h"
18
19 namespace NativeWindowSample {
20 using GetPlatformDisplayExt = PFNEGLGETPLATFORMDISPLAYEXTPROC;
21 constexpr const char *EGL_EXT_PLATFORM_WAYLAND = "EGL_EXT_platform_wayland";
22 constexpr const char *EGL_KHR_PLATFORM_WAYLAND = "EGL_KHR_platform_wayland";
23 constexpr int32_t EGL_CONTEXT_CLIENT_VERSION_NUM = 2;
24 constexpr char CHARACTER_WHITESPACE = ' ';
25 constexpr const char *CHARACTER_STRING_WHITESPACE = " ";
26 constexpr const char *EGL_GET_PLATFORM_DISPLAY_EXT = "eglGetPlatformDisplayEXT";
GetInstance()27 NativeImageAdaptor *NativeImageAdaptor::GetInstance()
28 {
29 static NativeImageAdaptor imageAdaptor;
30 return &imageAdaptor;
31 };
32
CheckEglExtension(const char * eglExtensions,const char * eglExtension)33 bool NativeImageAdaptor::CheckEglExtension(const char *eglExtensions, const char *eglExtension)
34 {
35 // Check egl extension
36 size_t extLenth = strlen(eglExtension);
37 const char *endPos = eglExtensions + strlen(eglExtensions);
38
39 while (eglExtensions < endPos) {
40 size_t len = 0;
41 if (*eglExtensions == CHARACTER_WHITESPACE) {
42 eglExtensions++;
43 continue;
44 }
45 len = strcspn(eglExtensions, CHARACTER_STRING_WHITESPACE);
46 if (len == extLenth && strncmp(eglExtension, eglExtensions, len) == 0) {
47 return true;
48 }
49 eglExtensions += len;
50 }
51 return false;
52 }
53
GetPlatformEglDisplay(EGLenum platform,void * native_display,const EGLint * attrib_list)54 EGLDisplay NativeImageAdaptor::GetPlatformEglDisplay(EGLenum platform, void *native_display, const EGLint *attrib_list)
55 {
56 static GetPlatformDisplayExt eglGetPlatformDisplayExt = NULL;
57 if (!eglGetPlatformDisplayExt) {
58 const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
59 if (extensions && (CheckEglExtension(extensions, EGL_EXT_PLATFORM_WAYLAND) ||
60 CheckEglExtension(extensions, EGL_KHR_PLATFORM_WAYLAND))) {
61 eglGetPlatformDisplayExt = (GetPlatformDisplayExt)eglGetProcAddress(EGL_GET_PLATFORM_DISPLAY_EXT);
62 }
63 }
64
65 if (eglGetPlatformDisplayExt) {
66 return eglGetPlatformDisplayExt(platform, native_display, attrib_list);
67 }
68
69 return eglGetDisplay((EGLNativeDisplayType)native_display);
70 }
71
InitEGLEnv()72 void NativeImageAdaptor::InitEGLEnv()
73 {
74 LOGD("NativeImageAdaptor::InitEGLEnv begin");
75 // Obtain the current display device
76 eglDisplay_ = GetPlatformEglDisplay(EGL_PLATFORM_OHOS_KHR, EGL_DEFAULT_DISPLAY, NULL);
77 if (eglDisplay_ == EGL_NO_DISPLAY) {
78 LOGE("NativeImageAdaptor::InitEGLEnv fail");
79 }
80
81 EGLint major, minor;
82 // Initialize EGLDisplay
83 if (eglInitialize(eglDisplay_, &major, &minor) == EGL_FALSE) {
84 LOGE("NativeImageAdaptor::eglInitialize fail");
85 }
86 // The API for binding graphic drawing is OpenGLES
87 if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) {
88 LOGE("eglBindAPI fail");
89 }
90 unsigned int ret;
91 EGLint count;
92 EGLint config_attribs[] = {EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE,
93 8, EGL_ALPHA_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, EGL_NONE};
94
95 // Obtain a valid system configuration information
96 ret = eglChooseConfig(eglDisplay_, config_attribs, &config_, 1, &count);
97 if (!(ret && static_cast<unsigned int>(count) >= 1)) {
98 LOGE("eglChooseConfig fail");
99 }
100 static const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, EGL_CONTEXT_CLIENT_VERSION_NUM, EGL_NONE};
101 eglContext_ = eglCreateContext(eglDisplay_, config_, EGL_NO_CONTEXT, context_attribs);
102 if (eglContext_ == EGL_NO_CONTEXT) {
103 LOGE("eglCreateContext fail");
104 }
105 // Associated Context
106 eglMakeCurrent(eglDisplay_, EGL_NO_SURFACE, EGL_NO_SURFACE, eglContext_);
107 LOGD("Create EGL context successfully");
108 }
109
Export(napi_env env,napi_value exports)110 bool NativeImageAdaptor::Export(napi_env env, napi_value exports)
111 {
112 napi_property_descriptor desc[] = {
113 {"GetAvailableCount", nullptr, NativeImageAdaptor::GetAvailableCount, nullptr, nullptr, nullptr, napi_default,
114 nullptr},
115 {"ProduceBuffer", nullptr, NativeImageAdaptor::NapiOnProduceBuffer, nullptr, nullptr, nullptr, napi_default,
116 nullptr},
117 };
118 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
119
120 eglContext_ = EGL_NO_CONTEXT;
121 eglDisplay_ = EGL_NO_DISPLAY;
122 availableBufferCount_ = 0;
123 // Creating OpenGL textures
124 InitEGLEnv();
125 GLuint textureId;
126 glGenTextures(1, &textureId);
127 // Create a NativeImage instance and associate it with OpenGL textures
128 image_ = OH_NativeImage_Create(textureId, GL_TEXTURE_2D);
129 // Obtain Producer NativeWindow
130 nativeWindow_ = OH_NativeImage_AcquireNativeWindow(image_);
131
132 int code = SET_BUFFER_GEOMETRY;
133 width_ = 0x100;
134 height_ = 0x100;
135 int32_t ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, code, width_, height_);
136 if (ret != 0) {
137 LOGE("NativeImageAdaptor OH_NativeWindow_NativeWindowHandleOpt fail");
138 }
139
140 code = SET_USAGE;
141 int32_t usage = NATIVEBUFFER_USAGE_CPU_READ | NATIVEBUFFER_USAGE_CPU_WRITE | NATIVEBUFFER_USAGE_MEM_DMA;
142 ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, code, usage);
143
144 OH_OnFrameAvailableListener listener;
145 listener.context = static_cast<void*>(image_);
146 listener.onFrameAvailable = NativeImageAdaptor::OnFrameAvailable;
147 ret = OH_NativeImage_SetOnFrameAvailableListener(image_, listener);
148
149 uint64_t surfaceId;
150 ret = OH_NativeImage_GetSurfaceId(image_, &surfaceId);
151 if (ret != 0) {
152 LOGE("OH_NativeImage_GetSurfaceId fail");
153 }
154 return true;
155 }
156
NapiOnProduceBuffer(napi_env env,napi_callback_info info)157 napi_value NativeImageAdaptor::NapiOnProduceBuffer(napi_env env, napi_callback_info info)
158 {
159 NativeImageAdaptor::GetInstance()->ProduceBuffer(0x00);
160 return nullptr;
161 }
162
ProduceBuffer(uint32_t value)163 void NativeImageAdaptor::ProduceBuffer(uint32_t value)
164 {
165 NativeWindowBuffer *buffer = nullptr;
166 int fenceFd = -1;
167 int ret = OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow_, &buffer, &fenceFd);
168 if (ret != 0) {
169 LOGE("OH_NativeWindow_NativeWindowRequestBuffer fail");
170 return;
171 }
172
173 BufferHandle *handle = OH_NativeWindow_GetBufferHandleFromNative(buffer);
174 // Obtain the memory virtual address of bufferHandle using the system mmap interface
175 void *mappedAddr = mmap(handle->virAddr, handle->size, PROT_READ | PROT_WRITE, MAP_SHARED, handle->fd, 0);
176 uint32_t *pixel = static_cast<uint32_t *>(mappedAddr);
177 for (uint32_t x = 0; x < width_; x++) {
178 for (uint32_t y = 0; y < height_; y++) {
179 *pixel++ = value;
180 }
181 }
182 // Remember to remove memory mapping after using memory
183 int result = munmap(mappedAddr, handle->size);
184 if (result == -1) {
185 LOGE("munmap fail");
186 }
187
188 struct Region *region = new Region();
189 struct Region::Rect *rect = new Region::Rect();
190 rect->x = 0x100;
191 rect->y = 0x100;
192 rect->w = 0x100;
193 rect->h = 0x100;
194 region->rects = rect;
195 ret = OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow_, buffer, fenceFd, *region);
196 if (ret != 0) {
197 LOGE("OH_NativeWindow_NativeWindowFlushBuffer fail");
198 return;
199 }
200 delete region;
201 }
202
OnFrameAvailable(void * context)203 void NativeImageAdaptor::OnFrameAvailable(void *context)
204 {
205 NativeImageAdaptor::GetInstance()->DealCallback(context);
206 return;
207 }
208
DealCallback(void * context)209 void NativeImageAdaptor::DealCallback(void *context)
210 {
211 std::lock_guard<std::mutex> lockGuard(opMutex_);
212 LOGD("NativeImageAdaptor success OnFrameAvailable, %{public}d", availableBufferCount_);
213 availableBufferCount_++;
214 int32_t ret = OH_NativeImage_UpdateSurfaceImage(image_);
215 if (ret != 0) {
216 LOGE("OH_NativeImage_UpdateSurfaceImage fail");
217 }
218 return;
219 }
220
GetCount()221 int32_t NativeImageAdaptor::GetCount()
222 {
223 std::lock_guard<std::mutex> lockGuard(opMutex_);
224 return availableBufferCount_;
225 }
226
GetAvailableCount(napi_env env,napi_callback_info info)227 napi_value NativeImageAdaptor::GetAvailableCount(napi_env env, napi_callback_info info)
228 {
229 napi_value val = nullptr;
230 int32_t count = NativeImageAdaptor::GetInstance()->GetCount();
231 (void)napi_create_int32(env, count, &val);
232 return val;
233 }
234
~NativeImageAdaptor()235 NativeImageAdaptor::~NativeImageAdaptor()
236 {
237 OH_NativeImage_UnsetOnFrameAvailableListener(image_);
238 OH_NativeWindow_DestroyNativeWindow(nativeWindow_);
239 OH_NativeImage_Destroy(&image_);
240 }
241 }