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