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 "native_avcodec_base.h"
17 #include <sstream>
18 #include <EGL/egl.h>
19 #include <GLES3/gl3.h>
20 #include <GLES3/gl31.h>
21 #include "external_window.h"
22 #include "native_averrors.h"
23 #include "graphics_render_engine_impl.h"
24 #include "render/graphics/base/gl_utils.h"
25 #include "render/graphics/base/render_context.h"
26 #include "render/graphics/base/render_surface.h"
27 #include "render/graphics/base/task/render_task.h"
28 #include "render/graphics/base/math/render_matrix.h"
29 #include "render/graphics/base/shader_pass/shader_pass_surface.h"
30 #include "render/graphics/base/shader_pass/shader_pass_on_screen.h"
31 #include "render/graphics/effect/image_effect_render.h"
32 #include "media_log.h"
33
34 namespace OHOS {
35 namespace Media {
36 namespace {
37 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_VIDEOEDITOR, "Render"};
38 }
GraphicsRenderEngineImpl(uint64_t id)39 GraphicsRenderEngineImpl::GraphicsRenderEngineImpl(uint64_t id) : id_(id)
40 {
41 MEDIA_LOGD("construct.");
42 }
43
~GraphicsRenderEngineImpl()44 GraphicsRenderEngineImpl::~GraphicsRenderEngineImpl()
45 {
46 MEDIA_LOGD("destroy begin.");
47 Destroy();
48 }
49
Destroy()50 void GraphicsRenderEngineImpl::Destroy()
51 {
52 std::lock_guard<std::recursive_mutex> guard(renderMutex_);
53 if (surface_ != nullptr) {
54 delete surface_;
55 surface_ = nullptr;
56 MEDIA_LOGI("Destroy graphics render surface");
57 }
58 if (!ready_) {
59 MEDIA_LOGW("graphics render engine[id=%{public}" PRIu64 "] is not ready, need not destroy.", id_);
60 return;
61 }
62 ready_ = false;
63 if (renderThread_) {
64 auto task =
65 std::make_shared<RenderTask<>>([this]() { this->ReleaseThread(); }, COMMON_TASK_TAG, RequestTaskId());
66 renderThread_->ClearTaskQueue();
67 renderThread_->AddTask(task);
68 task->Wait();
69 delete renderThread_;
70 renderThread_ = nullptr;
71 }
72 MEDIA_LOGI("[Render] Engine %{public}" PRIu64 " destroy end!", id_);
73 }
74
Init(OHNativeWindow * outputWindow)75 VEFError GraphicsRenderEngineImpl::Init(OHNativeWindow* outputWindow)
76 {
77 MEDIA_LOGI("init graphics engine.");
78 if (ready_) {
79 MEDIA_LOGW("[Render] graphics engine [id = %{public}" PRIu64 "] has been initialized,"
80 " please do not initialize again.", id_);
81 return VEFError::ERR_OK;
82 }
83
84 EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
85 if (!eglInitialize(display, nullptr, nullptr)) {
86 MEDIA_LOGE("unable to initialize display");
87 return VEFError::ERR_INTERNAL_ERROR;
88 }
89 MEDIA_LOGI("eglInitialize initialize finish");
90
91 VEFError ret = InitExportEngine(outputWindow);
92 if (ret != VEFError::ERR_OK) {
93 MEDIA_LOGE("init export engine failed with error: %{public}d.", ret);
94 return ret;
95 }
96
97 auto func = []() {};
98 renderThread_ = new (std::nothrow) RenderThread<>(RENDER_QUEUE_SIZE, func);
99 if (renderThread_ == nullptr) {
100 MEDIA_LOGE("create render thread failed.");
101 return VEFError::ERR_INTERNAL_ERROR;
102 }
103
104 renderThread_->Start();
105 VEFError error = VEFError::ERR_OK;
106 auto task = std::make_shared<RenderTask<>>(
107 [this, &error]() {
108 error = this->InitThread();
109 if (error != VEFError::ERR_OK) {
110 return;
111 }
112 error = this->InitSurfaceTexture();
113 if (error != VEFError::ERR_OK) {
114 return;
115 }
116
117 shaderPassSurface_ = std::make_shared<ShaderPassSurface>(context_.get());
118 shaderPassOnScreen_ = std::make_shared<ShaderPassOnScreen>(context_.get());
119 shaderPassRotate_ = std::make_shared<ShaderPassRotate>(context_.get());
120 ready_ = true;
121 },
122 COMMON_TASK_TAG, RequestTaskId());
123 renderThread_->AddTask(task);
124 task->Wait();
125
126 MEDIA_LOGI("initEnv finish");
127 return VEFError::ERR_OK;
128 }
129
StartRender()130 VEFError GraphicsRenderEngineImpl::StartRender()
131 {
132 MEDIA_LOGI("StartRender engine");
133 return VEFError::ERR_OK;
134 }
135
UnInit()136 VEFError GraphicsRenderEngineImpl::UnInit()
137 {
138 if (renderThread_ != nullptr) {
139 delete renderThread_;
140 renderThread_ = nullptr;
141 MEDIA_LOGI("uninit graphics engine");
142 }
143 if (surface_ != nullptr) {
144 delete surface_;
145 surface_ = nullptr;
146 MEDIA_LOGI("unInit graphics surface");
147 }
148 return VEFError::ERR_OK;
149 }
150
StopRender()151 VEFError GraphicsRenderEngineImpl::StopRender()
152 {
153 MEDIA_LOGI("StopRender engine");
154 if (ready_) {
155 renderThread_->ClearTaskQueue();
156 auto task =
157 std::make_shared<RenderTask<>>([this]() { this->ReleaseThread(); }, COMMON_TASK_TAG, RequestTaskId());
158 renderThread_->AddTask(task);
159 task->Wait();
160 }
161 return VEFError::ERR_OK;
162 }
163
InitExportEngine(OHNativeWindow * window)164 VEFError GraphicsRenderEngineImpl::InitExportEngine(OHNativeWindow* window)
165 {
166 MEDIA_LOGI("[Render] InitExportEngine %{public}" PRIu64 "begin", id_);
167 if (window == nullptr) {
168 MEDIA_LOGE("init export engine failed, the window is nullptr.");
169 return VEFError::ERR_INVALID_PARAM;
170 }
171
172 exportWindow_ = window;
173 surface_ = new (std::nothrow) RenderSurface(std::string());
174 if (surface_ == nullptr) {
175 MEDIA_LOGE("create RenderSurface object failed.");
176 return VEFError::ERR_OOM;
177 }
178
179 surface_->SetAttrib(attribute_);
180 surface_->Create(window);
181
182 MEDIA_LOGI("[Render] InitExportEngine %{public}" PRIu64 "success", id_);
183 return VEFError::ERR_OK;
184 }
185
GetThreadId() const186 std::string GraphicsRenderEngineImpl::GetThreadId() const
187 {
188 std::ostringstream stream;
189 stream << std::this_thread::get_id();
190 return stream.str();
191 }
192
InitThread()193 VEFError GraphicsRenderEngineImpl::InitThread()
194 {
195 std::string threadID = GetThreadId();
196 MEDIA_LOGI("init render thread [%{public}s].", threadID.c_str());
197
198 auto context = std::make_shared<RenderContext>();
199 if (!context->Init()) {
200 MEDIA_LOGE("init render context failed");
201 return VEFError::ERR_INTERNAL_ERROR;
202 }
203 if (!context->MakeCurrent(surface_)) {
204 MEDIA_LOGE("make egl context to current context failed");
205 return VEFError::ERR_INTERNAL_ERROR;
206 }
207 context_ = context;
208 MEDIA_LOGI("init render thread [%{public}s] success.", threadID.c_str());
209 return VEFError::ERR_OK;
210 }
211
ReleaseThread()212 void GraphicsRenderEngineImpl::ReleaseThread()
213 {
214 std::string threadId = GetThreadId();
215 MEDIA_LOGD("ReleaseThread: %{public}s", threadId.c_str());
216 }
217
RequestTaskId()218 uint64_t GraphicsRenderEngineImpl::RequestTaskId()
219 {
220 return currentTaskId_.fetch_add(1);
221 }
222
InitSurfaceTexture()223 VEFError GraphicsRenderEngineImpl::InitSurfaceTexture()
224 {
225 MEDIA_LOGI("init render surface texture.");
226 VEFError result = VEFError::ERR_INTERNAL_ERROR;
227 GLuint oesTexId = GL_NONE;
228 do {
229 auto surfaceTexture = std::make_shared<SurfaceTexture>();
230 if (!context_->MakeCurrent(surface_)) {
231 MEDIA_LOGE("make egl context to current context failed.");
232 break;
233 }
234 oesTexId = GLUtils::CreateTexNoStorage(GL_TEXTURE_EXTERNAL_OES);
235 if (oesTexId == GL_NONE) {
236 MEDIA_LOGE("create oes texture object failed.");
237 break;
238 }
239 surfaceTexture->nativeTexId_ = oesTexId;
240 auto nativeImage = OH_NativeImage_Create(oesTexId, GL_TEXTURE_EXTERNAL_OES);
241 if (nativeImage == nullptr) {
242 MEDIA_LOGE("create nativeImage object failed.");
243 break;
244 }
245 surfaceTexture->nativeImage_ =
246 std::shared_ptr<OH_NativeImage>(nativeImage, [](OH_NativeImage* image) { OH_NativeImage_Destroy(&image); });
247 surfaceTexture->nativeWindow_ = OH_NativeImage_AcquireNativeWindow(surfaceTexture->nativeImage_.get());
248 if (surfaceTexture->nativeWindow_ == nullptr) {
249 MEDIA_LOGE("OH_NativeImage_AcquireNativeWindow failed.");
250 break;
251 }
252 OH_OnFrameAvailableListener listener;
253 listener.context = surfaceTexture.get();
254 listener.onFrameAvailable = SurfaceTexture::OnFrameAvailable;
255 int32_t error = OH_NativeImage_SetOnFrameAvailableListener(surfaceTexture->nativeImage_.get(), listener);
256 if (error != 0) {
257 MEDIA_LOGE("OH_NativeImage_SetOnFrameAvailableListener failed with error:%{public}d.", error);
258 break;
259 }
260 surfaceTexture_ = surfaceTexture;
261 result = VEFError::ERR_OK;
262 MEDIA_LOGI("init render surface texture success.");
263 } while (false);
264 if (result != VEFError::ERR_OK) {
265 GLUtils::DeleteTexture(oesTexId);
266 }
267 return result;
268 }
269
GetInputWindow()270 OHNativeWindow* GraphicsRenderEngineImpl::GetInputWindow()
271 {
272 if (surfaceTexture_ != nullptr) {
273 return surfaceTexture_->nativeWindow_;
274 }
275 return nullptr;
276 }
277
Render(uint64_t index,const std::shared_ptr<GraphicsRenderInfo> & effectInfo,const RenderResultCallback & cb)278 VEFError GraphicsRenderEngineImpl::Render(uint64_t index, const std::shared_ptr<GraphicsRenderInfo>& effectInfo,
279 const RenderResultCallback& cb)
280 {
281 std::lock_guard<std::recursive_mutex> guard(renderMutex_);
282 if (ready_) {
283 MEDIA_LOGD("render frame[index = %{public}" PRIu64 "]", index);
284 } else {
285 MEDIA_LOGE("render engine not ready render frame[index = %{public}" PRIu64 "]failed", index);
286 return VEFError::ERR_INTERNAL_ERROR;
287 }
288 auto taskId = RequestTaskId();
289 auto launchTime = std::chrono::high_resolution_clock::now();
290 auto task = std::make_shared<RenderTask<>>(
291 [this, taskId, launchTime, index, effectInfo, cb]() {
292 auto beginTime = std::chrono::high_resolution_clock::now();
293 this->context_->MakeCurrent(surface_);
294 auto renderTexture = this->RenderFrame(effectInfo);
295 auto renderEndTime = std::chrono::high_resolution_clock::now();
296 this->DrawFrame(index, renderTexture);
297 auto drawEndtime = std::chrono::high_resolution_clock::now();
298 std::chrono::duration<double, std::milli> queueDelay = beginTime - launchTime;
299 std::chrono::duration<double, std::milli> renderTime = renderEndTime - beginTime;
300 std::chrono::duration<double, std::milli> drawtime = drawEndtime - renderEndTime;
301 MEDIA_LOGD("renderTime: %{public}lf frawTime: %{public}lf,"
302 " queueDelay: %{public}lf, TaskId: %{public}" PRIu64 ","
303 "ExternId: %{public}" PRIu64 ".", renderTime.count(), drawtime.count(),
304 queueDelay.count(), taskId, index);
305 cb(GraphicsRenderResult::SUCCESS);
306 },
307 EXPORT_TASK_TAG, taskId);
308
309 renderThread_->AddTask(task);
310 return VEFError::ERR_OK;
311 }
312
RenderFrame(const std::shared_ptr<GraphicsRenderInfo> & graphicsRenderInfo)313 RenderTexturePtr GraphicsRenderEngineImpl::RenderFrame(const std::shared_ptr<GraphicsRenderInfo>& graphicsRenderInfo)
314 {
315 surfaceTexture_->AwaitNativeImage();
316 GLUtils::CheckError(__FILE_NAME__, __LINE__);
317 if (auto ret = OH_NativeImage_UpdateSurfaceImage(surfaceTexture_->nativeImage_.get()); ret != 0) {
318 MEDIA_LOGE("RenderFrame OH_NativeImage_UpdateSurfaceImage failed with error:%{public}d.", ret);
319 }
320 GLint bufferW = -1;
321 GLint bufferH = -1;
322 glBindTexture(GL_TEXTURE_EXTERNAL_OES, surfaceTexture_->nativeTexId_);
323 glGetTexLevelParameteriv(GL_TEXTURE_EXTERNAL_OES, 0, GL_TEXTURE_WIDTH, &bufferW);
324 glGetTexLevelParameteriv(GL_TEXTURE_EXTERNAL_OES, 0, GL_TEXTURE_HEIGHT, &bufferH);
325 glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
326 int32_t rotation = graphicsRenderInfo->rotation_;
327 Mat4x4 matrix = GLUtils::GetOesSamplingMatrix({ bufferW, bufferH }, { bufferW, bufferH }, rotation, false);
328 SurfaceDataPtr surfaceData = shaderPassSurface_->GetRenderEffectData();
329 surfaceData->nativeTexId = surfaceTexture_->nativeTexId_;
330 surfaceData->uTexMatrix = matrix;
331 const int32_t rotation90 = 90;
332 const int32_t rotation270 = 270;
333 if (rotation == rotation90 || rotation == rotation270) {
334 surfaceData->outputWidth_ = bufferH;
335 surfaceData->outputHeight_ = bufferW;
336 } else {
337 surfaceData->outputWidth_ = bufferW;
338 surfaceData->outputHeight_ = bufferH;
339 }
340 auto outputRenderTexture = shaderPassSurface_->Render();
341 outputRenderTexture = RenderEffects(outputRenderTexture, graphicsRenderInfo);
342 if (rotation != 0) {
343 Mat4x4 rotateMatrix = GLUtils::GetOesSamplingMatrix(
344 {bufferW, bufferH},
345 { bufferW, bufferH },
346 -rotation,
347 false);
348 SurfaceDataPtr rotateSurfaceData = shaderPassRotate_->GetRenderEffectData();
349 rotateSurfaceData->inputTexture_ = outputRenderTexture;
350 rotateSurfaceData->outputWidth_ = bufferW;
351 rotateSurfaceData->outputHeight_ = bufferH;
352 rotateSurfaceData->uTexMatrix = rotateMatrix;
353 auto outputTexture = shaderPassRotate_->Render();
354 return outputTexture;
355 } else {
356 return outputRenderTexture;
357 }
358 }
359
RenderEffects(const RenderTexturePtr & inputTexture,const std::shared_ptr<GraphicsRenderInfo> & graphicsRenderInfo)360 RenderTexturePtr GraphicsRenderEngineImpl::RenderEffects(const RenderTexturePtr& inputTexture,
361 const std::shared_ptr<GraphicsRenderInfo>& graphicsRenderInfo)
362 {
363 if (graphicsRenderInfo == nullptr) {
364 MEDIA_LOGD("renderInfo is nullptr, not need render effect.");
365 return inputTexture;
366 }
367 int32_t colorRange = graphicsRenderInfo->colorRange_;
368 auto outputRenderTexture = inputTexture;
369 for (const auto& effectInfo : graphicsRenderInfo->effectInfoList_) {
370 if (effectInfo->type != EffectType::IMAGE_EFFECT) {
371 MEDIA_LOGE("effectType [%{public}d] is not supported.", effectInfo->type);
372 continue;
373 }
374 auto it = imageEffectRenderList_.find(effectInfo->id);
375 if (it != imageEffectRenderList_.end()) {
376 outputRenderTexture = it->second->Render(context_.get(), surface_, outputRenderTexture, colorRange);
377 } else {
378 #ifdef IMAGE_EFFECT_SUPPORT
379 auto imageEffectRender = std::make_shared<ImageEffectRender>(effectInfo->imageEffect);
380 imageEffectRenderList_[effectInfo->id] = imageEffectRender;
381 outputRenderTexture = imageEffectRender->Render(context_.get(), surface_, outputRenderTexture, colorRange);
382 #endif
383 }
384 }
385
386 return outputRenderTexture;
387 }
388
DrawFrame(uint64_t pts,const RenderTexturePtr & renderTexture)389 void GraphicsRenderEngineImpl::DrawFrame(uint64_t pts, const RenderTexturePtr& renderTexture)
390 {
391 auto retCode = OH_NativeWindow_NativeWindowHandleOpt(exportWindow_, SET_UI_TIMESTAMP, pts);
392 if (retCode != AV_ERR_OK) {
393 MEDIA_LOGE("set native window opt faild, retcode = %{public}d", retCode);
394 }
395 RenderEffectDataPtr renderEffectData = shaderPassOnScreen_->GetRenderEffectData();
396 if (renderTexture == nullptr) {
397 MEDIA_LOGE("DrawFrame param renderTexture is nullptr.");
398 return;
399 }
400 renderEffectData->inputTexture_ = renderTexture;
401 renderEffectData->outputWidth_ = renderTexture->Width();
402 renderEffectData->outputHeight_ = renderTexture->Height();
403 shaderPassOnScreen_->Render();
404 context_->SwapBuffers(surface_);
405 }
406 }
407 }