• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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