• 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 <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