1 /*
2 * Copyright (c) 2024 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 "render_engine.h"
17 #include <hilog/log.h>
18 #include <common/common.h>
19 #include <GLES2/gl2.h>
20 #include <GLES2/gl2ext.h>
21 #include <mutex>
22 #include <condition_variable>
23 #include <unistd.h>
24
25 namespace {
26 constexpr int FRAME_RATE = 60; // 目标帧率
27 constexpr int NANOSECONDS_IN_A_SECOND = 1000000000; // 一秒中的纳秒数
28 }
29
RenderEngine(std::shared_ptr<ImageRender> imageRender,uint64_t width,uint64_t height,void * window)30 RenderEngine::RenderEngine(std::shared_ptr<ImageRender> imageRender, uint64_t width, uint64_t height, void *window)
31 : imageRender_(std::move(imageRender)), width_(width), height_(height), window_(window)
32 {
33 Start();
34 }
35
~RenderEngine()36 RenderEngine::~RenderEngine()
37 {
38 Stop();
39 }
40
Start()41 void RenderEngine::Start()
42 {
43 if (running_) {
44 return;
45 }
46 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RenderEngine", "start.");
47 running_ = true;
48
49 thread_ = std::thread([this]() {
50 if (!imageRender_->InitEGL(reinterpret_cast<EGLNativeWindowType>(window_), width_, height_)) {
51 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine", "Failed to initialize EGL");
52 return;
53 }
54 if (!InitNativeVsync()) {
55 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine", "Failed to initialize VSync");
56 return;
57 }
58 if (!CreateNativeImage()) {
59 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine", "Failed to create Native Image");
60 return;
61 }
62 if (!StartNativeRenderThread()) {
63 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine", "Failed to start NativeRender thread");
64 return;
65 }
66 MainLoop();
67 CleanupResources();
68 });
69 }
70
CleanupResources()71 void RenderEngine::CleanupResources()
72 {
73 StopNativeRenderThread();
74 DestroyNativeImage();
75 DestroyNativeVsync();
76 imageRender_->Cleanup();
77 }
78
Stop()79 void RenderEngine::Stop()
80 {
81 if (!running_) {
82 return;
83 }
84
85 running_ = false;
86
87 if (thread_.joinable()) {
88 thread_.join();
89 }
90
91 CleanupResources();
92 }
93
UpdateNativeWindow(uint64_t width,uint64_t height)94 void RenderEngine::UpdateNativeWindow(uint64_t width, uint64_t height)
95 {
96 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RenderEngine", "UpdateNativeWindow.");
97 PostTask([this, width, height](std::shared_ptr<ImageRender> imageRender) {
98 imageRender->UpdateWindowInfo(width, height);
99 imageRender->UpdateViewport();
100 });
101 }
102
MainLoop()103 void RenderEngine::MainLoop()
104 {
105 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RenderEngine", "MainLoop start.");
106
107 threadId_ = std::this_thread::get_id();
108
109 while (running_) {
110 WaitForNewFrame();
111 if (!isPaused_) {
112 ExecuteRenderTasks();
113 UpdateSurfaceImage();
114 }
115 }
116
117 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RenderEngine", "MainLoop end.");
118 }
119
WaitForNewFrame()120 void RenderEngine::WaitForNewFrame()
121 {
122 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RenderEngine", "Waiting for new frame.");
123 std::unique_lock<std::mutex> lock(frameMutex_);
124 frameCond_.wait(lock, [this] { return availableFrameCnt_ > 0 || !running_; });
125 }
126
WaitForVsync()127 void RenderEngine::WaitForVsync()
128 {
129 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RenderEngine", "Waiting for vsync.");
130 std::unique_lock<std::mutex> lock(wakeUpMutex_);
131 wakeUpCond_.wait(lock, [this]() { return wakeUp_ || vSyncCnt_ > 0; });
132 wakeUp_ = false;
133 if (vSyncCnt_ > 0) {
134 vSyncCnt_--;
135 (void)OH_NativeVSync_RequestFrame(nativeVsync_, &RenderEngine::OnVsync, this);
136 }
137 }
138
OnVsync(long long timestamp,void * data)139 void RenderEngine::OnVsync(long long timestamp, void *data)
140 {
141 OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_PRINT_DOMAIN, "RenderEngine", "OnVsync %{public}lld.", timestamp);
142 auto renderEngine = reinterpret_cast<RenderEngine *>(data);
143 if (renderEngine == nullptr) {
144 return;
145 }
146
147 renderEngine->vSyncCnt_++;
148 renderEngine->wakeUpCond_.notify_one();
149 }
150
InitNativeVsync()151 bool RenderEngine::InitNativeVsync()
152 {
153 const char* demoName = "NativeImageSample";
154 nativeVsync_ = OH_NativeVSync_Create(demoName, strlen(demoName));
155 if (nativeVsync_ == nullptr) {
156 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine", "Create NativeVSync failed.");
157 return false;
158 }
159 (void)OH_NativeVSync_RequestFrame(nativeVsync_, &RenderEngine::OnVsync, this);
160 return true;
161 }
162
DestroyNativeVsync()163 void RenderEngine::DestroyNativeVsync()
164 {
165 if (nativeVsync_ != nullptr) {
166 OH_NativeVSync_Destroy(nativeVsync_);
167 nativeVsync_ = nullptr;
168 }
169 }
170
ExecuteRenderTasks()171 void RenderEngine::ExecuteRenderTasks()
172 {
173 std::vector<RenderTask> tasks;
174 {
175 std::lock_guard<std::mutex> lock(taskMutex_);
176 tasks.swap(tasks_);
177 }
178 for (const auto &task : tasks) {
179 task(imageRender_);
180 }
181 }
182
UpdateSurfaceImage()183 void RenderEngine::UpdateSurfaceImage()
184 {
185 if (availableFrameCnt_ <= 0) {
186 return;
187 }
188
189 if (!nativeImage_) {
190 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine", "Native Image is null");
191 return;
192 }
193
194 int32_t ret = OH_NativeImage_UpdateSurfaceImage(nativeImage_);
195 if (ret != 0) {
196 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine",
197 "OH_NativeImage_UpdateSurfaceImage failed, ret: %{public}d, texId: %{public}u",
198 ret, nativeImageTexId_);
199 return;
200 }
201
202 UpdateTextureMatrix();
203 if (imageRender_) {
204 imageRender_->Render();
205 } else {
206 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine", "ImageRender is null");
207 }
208 availableFrameCnt_--;
209 }
210
UpdateTextureMatrix()211 void RenderEngine::UpdateTextureMatrix()
212 {
213 float matrix[16];
214 int32_t ret = OH_NativeImage_GetTransformMatrixV2(nativeImage_, matrix);
215 if (ret != 0) {
216 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine",
217 "OH_NativeImage_GetTransformMatrix failed, ret: %{public}d", ret);
218 return;
219 }
220 imageRender_->SetTransformMatrix(matrix);
221 }
222
CreateNativeImage()223 bool RenderEngine::CreateNativeImage()
224 {
225 if (!GenerateTexture() || !CreateNativeImageObject() || !AttachContextAndGetSurfaceId()) {
226 return false;
227 }
228
229 if (!SetFrameAvailableListener()) {
230 return false;
231 }
232
233 imageRender_->SetTexture(nativeImageTexId_, GL_TEXTURE_EXTERNAL_OES);
234
235 return true;
236 }
237
GenerateTexture()238 bool RenderEngine::GenerateTexture()
239 {
240 glGenTextures(1, &nativeImageTexId_);
241 glBindTexture(GL_TEXTURE_EXTERNAL_OES, nativeImageTexId_);
242 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
243 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
244 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
245 glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
246
247 return true;
248 }
249
CreateNativeImageObject()250 bool RenderEngine::CreateNativeImageObject()
251 {
252 nativeImage_ = OH_NativeImage_Create(nativeImageTexId_, GL_TEXTURE_EXTERNAL_OES);
253 if (nativeImage_ == nullptr) {
254 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine", "Failed to create Native Image");
255 return false;
256 }
257 return true;
258 }
259
AttachContextAndGetSurfaceId()260 bool RenderEngine::AttachContextAndGetSurfaceId()
261 {
262 int ret = OH_NativeImage_AttachContext(nativeImage_, nativeImageTexId_);
263 if (ret != 0) {
264 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine",
265 "AttachContext failed for textureId: %{public}u, ret: %{public}d", nativeImageTexId_, ret);
266 return false;
267 }
268
269 std::lock_guard<std::mutex> lock(nativeImageSurfaceIdMutex_);
270 ret = OH_NativeImage_GetSurfaceId(nativeImage_, &nativeImageSurfaceId_);
271 if (ret != 0) {
272 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine",
273 "OH_NativeImage_GetSurfaceId failed, ret: %{public}d", ret);
274 return false;
275 }
276 return true;
277 }
278
SetFrameAvailableListener()279 bool RenderEngine::SetFrameAvailableListener()
280 {
281 nativeImageFrameAvailableListener_.context = this;
282 nativeImageFrameAvailableListener_.onFrameAvailable = &RenderEngine::OnNativeImageFrameAvailable;
283 int ret = OH_NativeImage_SetOnFrameAvailableListener(nativeImage_, nativeImageFrameAvailableListener_);
284 if (ret != 0) {
285 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine",
286 "OH_NativeImage_SetOnFrameAvailableListener failed, ret: %{public}d", ret);
287 return false;
288 }
289 return true;
290 }
291
PostTask(const RenderTask & task)292 void RenderEngine::PostTask(const RenderTask &task)
293 {
294 if (!running_) {
295 OH_LOG_Print(LOG_APP, LOG_WARN, LOG_PRINT_DOMAIN, "RenderEngine",
296 "PostTask failed: RenderEngine is not running");
297 return;
298 }
299
300 {
301 std::lock_guard<std::mutex> lock(taskMutex_);
302 tasks_.push_back(task);
303 }
304
305 frameCond_.notify_one();
306 }
307
OnNativeImageFrameAvailable(void * data)308 void RenderEngine::OnNativeImageFrameAvailable(void *data)
309 {
310 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RenderEngine", "OnNativeImageFrameAvailable.");
311 auto renderEngine = reinterpret_cast<RenderEngine *>(data);
312 if (renderEngine == nullptr) {
313 return;
314 }
315 {
316 std::lock_guard<std::mutex> lock(renderEngine->frameMutex_);
317 renderEngine->availableFrameCnt_++;
318 }
319 renderEngine->frameCond_.notify_one();
320 }
321
DestroyNativeImage()322 void RenderEngine::DestroyNativeImage()
323 {
324 if (nativeImageTexId_ != 0U) {
325 glDeleteTextures(1, &nativeImageTexId_);
326 nativeImageTexId_ = 0U;
327 }
328
329 if (nativeImage_ != nullptr) {
330 (void)OH_NativeImage_UnsetOnFrameAvailableListener(nativeImage_);
331 OH_NativeImage_Destroy(&nativeImage_);
332 nativeImage_ = nullptr;
333 }
334 }
335
StartNativeRenderThread()336 bool RenderEngine::StartNativeRenderThread()
337 {
338 if (!nativeRender_) {
339 nativeRender_ = std::make_shared<OHNativeRender>();
340 }
341
342 {
343 std::lock_guard<std::mutex> lock(nativeImageSurfaceIdMutex_);
344 if (nativeImageSurfaceId_ == 0) {
345 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine", "Invalid nativeImageSurfaceId_");
346 return false;
347 }
348
349 if (!nativeRender_->SetSurfaceId(nativeImageSurfaceId_, width_, height_)) {
350 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN,
351 "RenderEngine", "Failed to set SurfaceId for NativeRender");
352 return false;
353 }
354 }
355
356 nativeRenderThread_ = std::thread([this]() {
357 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RenderEngine", "Native Render Thread started");
358 while (running_) {
359 // 等待 VSync 信号
360 WaitForVsync();
361 if (!isPaused_ && nativeRender_) {
362 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RenderEngine", "Rendering Frame");
363 nativeRender_->RenderFrame();
364 }
365 }
366 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RenderEngine", "Native Render Thread ended");
367 });
368
369 return true;
370 }
371
StopNativeRenderThread()372 void RenderEngine::StopNativeRenderThread()
373 {
374 if (nativeRenderThread_.joinable()) {
375 nativeRenderThread_.join();
376 }
377 }
378
OnSurfaceChanged(uint64_t width,uint64_t height)379 void RenderEngine::OnSurfaceChanged(uint64_t width, uint64_t height)
380 {
381 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RenderEngine",
382 "Surface changed to %{public}llu x %{public}lu", width, height);
383
384 UpdateNativeWindow(width, height);
385 }
386
OnAppResume()387 void RenderEngine::OnAppResume()
388 {
389 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RenderEngine", "App resumed");
390 isPaused_ = false;
391 }
392
OnAppPause()393 void RenderEngine::OnAppPause()
394 {
395 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RenderEngine", "App paused");
396 isPaused_ = true;
397 }
398