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 "scene_adapter/scene_adapter.h"
17
18 #include <dlfcn.h>
19 #include <memory>
20 #include <string_view>
21
22 #include "napi/native_api.h"
23 #include "napi/native_node_api.h"
24
25 #include <base/containers/array_view.h>
26
27 #include <core/intf_engine.h>
28 #include <core/ecs/intf_system_graph_loader.h>
29 #include <core/engine_info.h>
30 #include <core/implementation_uids.h>
31 #include <core/io/intf_file_manager.h>
32 #include <core/namespace.h>
33 #include <core/os/intf_platform.h>
34 #include <core/plugin/intf_plugin_register.h>
35 #include <core/property/intf_property_handle.h>
36
37 #include <meta/interface/intf_meta_object_lib.h>
38 #include <meta/interface/intf_task_queue_registry.h>
39 #include <meta/interface/intf_task_queue.h>
40 #include <meta/interface/intf_object.h>
41 #include <meta/interface/intf_object_registry.h>
42 #include <meta/interface/intf_task_queue.h>
43 #include <meta/base/shared_ptr.h>
44 #include <meta/base/interface_macros.h>
45 #include <meta/api/make_callback.h>
46 #include <meta/ext/object.h>
47
48 #include <scene_plugin/namespace.h>
49 #include <scene_plugin/interface/intf_scene.h>
50 #include <scene_plugin/interface/intf_ecs_scene.h>
51 #include <scene_plugin/interface/intf_mesh.h>
52 #include <scene_plugin/interface/intf_material.h>
53 #include <scene_plugin/api/scene_uid.h>
54
55 #include "ability.h"
56 #include "data_ability_helper.h"
57 #include "napi_base_context.h"
58
59 #include <render/implementation_uids.h>
60 #include <render/gles/intf_device_gles.h>
61 #include <render/intf_renderer.h>
62 #include <render/intf_render_context.h>
63
64 #include "3d_widget_adapter_log.h"
65 #include "widget_trace.h"
66 #include "ohos/texture_layer.h"
67
68 namespace OHOS::Render3D {
GetHapInfo()69 HapInfo GetHapInfo()
70 {
71 std::shared_ptr<AbilityRuntime::ApplicationContext> context =
72 AbilityRuntime::ApplicationContext::GetApplicationContext();
73 if (!context) {
74 WIDGET_LOGE("Failed to get application context.");
75 return {};
76 }
77 auto resourceManager = context->GetResourceManager();
78 if (!resourceManager) {
79 WIDGET_LOGE("Failed to get resource manager.");
80 return {};
81 }
82 HapInfo hapInfo;
83 hapInfo.bundleName_ = resourceManager->bundleInfo.first;
84 hapInfo.moduleName_ = resourceManager->bundleInfo.second;
85
86 // hapPath
87 std::string hapPath = context->GetBundleCodeDir();
88 hapInfo.hapPath_ = hapPath + "/" + hapInfo.moduleName_ + ".hap";
89 WIDGET_LOGD("bundle %s, module %s, hapPath %s",
90 hapInfo.bundleName_.c_str(),
91 hapInfo.moduleName_.c_str(),
92 hapInfo.hapPath_.c_str());
93
94 return hapInfo;
95 }
96
97 using IntfPtr = META_NS::SharedPtrIInterface;
98 using IntfWeakPtr = META_NS::WeakPtrIInterface;
99
100 struct EngineInstance {
101 void *libHandle_ = nullptr;
102 BASE_NS::shared_ptr<RENDER_NS::IRenderContext> renderContext_;
103 BASE_NS::shared_ptr<CORE_NS::IEngine> engine_;
104 };
105
106 static EngineInstance engineInstance_;
107 static std::mutex mute;
108 META_NS::ITaskQueue::Ptr engineThread;
109 META_NS::ITaskQueue::Ptr ioThread;
110
LockCompositor()111 void LockCompositor()
112 {
113 mute.lock();
114 }
115
UnlockCompositor()116 void UnlockCompositor()
117 {
118 mute.unlock();
119 }
120
121 static constexpr BASE_NS::Uid ENGINE_THREAD { "2070e705-d061-40e4-bfb7-90fad2c280af" };
122 static constexpr BASE_NS::Uid APP_THREAD { "b2e8cef3-453a-4651-b564-5190f8b5190d" };
123 static constexpr BASE_NS::Uid IO_QUEUE { "be88e9a0-9cd8-45ab-be48-937953dc258f" };
124
125 template<typename T>
LoadFunc(T & fn,const char * fName,void * handle)126 bool LoadFunc(T &fn, const char *fName, void* handle)
127 {
128 fn = reinterpret_cast<T>(dlsym(handle, fName));
129 if (fn == nullptr) {
130 WIDGET_LOGE("%s open %s", __func__, dlerror());
131 return false;
132 }
133 return true;
134 }
135
~SceneAdapter()136 SceneAdapter::~SceneAdapter()
137 {
138 }
139
SceneAdapter()140 SceneAdapter::SceneAdapter()
141 {
142 WIDGET_LOGD("scene adapter Impl create");
143 }
144
LoadEngineLib()145 bool SceneAdapter::LoadEngineLib()
146 {
147 if (engineInstance_.libHandle_ != nullptr) {
148 WIDGET_LOGD("%s, already loaded", __func__);
149 return true;
150 }
151
152 #define TO_STRING(name) #name
153 #define LIB_NAME(name) TO_STRING(name)
154 constexpr std::string_view lib { LIB_NAME(LIB_ENGINE_CORE)".so" };
155 engineInstance_.libHandle_ = dlopen(lib.data(), RTLD_LAZY);
156
157 if (engineInstance_.libHandle_ == nullptr) {
158 WIDGET_LOGE("%s, open lib fail %s", __func__, dlerror());
159 }
160 #undef TO_STRING
161 #undef LIB_NAME
162
163 #define LOAD_FUNC(fn, name) LoadFunc<decltype(fn)>(fn, name, engineInstance_.libHandle_)
164 if (!(LOAD_FUNC(CORE_NS::CreatePluginRegistry,
165 "_ZN4Core20CreatePluginRegistryERKNS_18PlatformCreateInfoE")
166 && LOAD_FUNC(CORE_NS::GetPluginRegister, "_ZN4Core17GetPluginRegisterEv")
167 && LOAD_FUNC(CORE_NS::IsDebugBuild, "_ZN4Core12IsDebugBuildEv")
168 && LOAD_FUNC(CORE_NS::GetVersion, "_ZN4Core13GetVersionRevEv"))) {
169 return false;
170 }
171 #undef LOAD_FUNC
172
173 return true;
174 }
175
LoadPlugins(const CORE_NS::PlatformCreateInfo & platformCreateInfo)176 bool SceneAdapter::LoadPlugins(const CORE_NS::PlatformCreateInfo& platformCreateInfo)
177 {
178 if (engineInstance_.engine_) {
179 return true;
180 }
181 if (!LoadEngineLib()) {
182 return false;
183 }
184 WIDGET_LOGD("load engine success!");
185 const BASE_NS::Uid DefaultPluginList[] { SCENE_NS::UID_SCENE_PLUGIN };
186
187 CORE_NS::CreatePluginRegistry(platformCreateInfo);
188 if (!CORE_NS::GetPluginRegister().LoadPlugins(DefaultPluginList)) {
189 WIDGET_LOGE("fail to load scene widget plugin");
190 return false;
191 }
192 WIDGET_LOGD("load plugin success");
193 return true;
194 }
195
InitEngine(CORE_NS::PlatformCreateInfo platformCreateInfo)196 bool SceneAdapter::InitEngine(CORE_NS::PlatformCreateInfo platformCreateInfo)
197 {
198 if (engineInstance_.engine_) {
199 return true;
200 }
201 auto& tr = META_NS::GetTaskQueueRegistry();
202 auto& obr = META_NS::GetObjectRegistry();
203
204 engineThread = tr.GetTaskQueue(ENGINE_THREAD);
205 if (!engineThread) {
206 engineThread = obr.Create<META_NS::ITaskQueue>(META_NS::ClassId::ThreadedTaskQueue);
207 tr.RegisterTaskQueue(engineThread, ENGINE_THREAD);
208 }
209 ioThread = tr.GetTaskQueue(IO_QUEUE);
210 if (!ioThread) {
211 ioThread = obr.Create<META_NS::ITaskQueue>(META_NS::ClassId::ThreadedTaskQueue);
212 tr.RegisterTaskQueue(ioThread, IO_QUEUE);
213 }
214
215 auto engineInit = META_NS::MakeCallback<META_NS::ITaskQueueWaitableTask>([platformCreateInfo]() {
216 auto& obr = META_NS::GetObjectRegistry();
217 // Initialize lumeengine/render etc
218 CORE_NS::EngineCreateInfo engineCreateInfo { platformCreateInfo, {}, {} };
219 if (auto factory = CORE_NS::GetInstance<CORE_NS::IEngineFactory>(CORE_NS::UID_ENGINE_FACTORY)) {
220 engineInstance_.engine_.reset(factory->Create(engineCreateInfo).get());
221 }
222
223 if (!engineInstance_.engine_) {
224 WIDGET_LOGE("get engine fail");
225 return META_NS::IAny::Ptr {};
226 }
227 engineInstance_.engine_->Init();
228
229 engineInstance_.renderContext_.reset(
230 CORE_NS::CreateInstance<RENDER_NS::IRenderContext>(*engineInstance_.engine_, RENDER_NS::UID_RENDER_CONTEXT)
231 .get());
232
233 RENDER_NS::RenderCreateInfo renderCreateInfo;
234 // this context needs to be "not busy" (not selected current in any thread) for creation to succeed.
235 std::string backendProp = "gles";
236 if (backendProp == "vulkan") {
237 } else {
238 Render::DeviceCreateInfo deviceCreateInfo;
239 RENDER_NS::BackendExtraGLES glExtra;
240 glExtra.depthBits = 24; // 24 : bit size
241 glExtra.sharedContext = EGL_NO_CONTEXT;
242 deviceCreateInfo.backendType = RENDER_NS::DeviceBackendType::OPENGLES;
243 deviceCreateInfo.backendConfiguration = &glExtra;
244 renderCreateInfo.applicationInfo = {};
245 renderCreateInfo.deviceCreateInfo = deviceCreateInfo;
246 }
247
248 auto rrc = engineInstance_.renderContext_->Init(renderCreateInfo);
249 if (rrc != RENDER_NS::RenderResultCode::RENDER_SUCCESS) {
250 WIDGET_LOGE("Failed to create render context");
251 return META_NS::IAny::Ptr {};
252 }
253
254 // Save the stuff to the default object context.
255 auto engineThread = META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD);
256 // thats the javascript thread...
257 auto appThread = engineThread;
258 auto doc = interface_cast<META_NS::IMetadata>(obr.GetDefaultObjectContext());
259 auto flags = META_NS::ObjectFlagBits::INTERNAL | META_NS::ObjectFlagBits::NATIVE;
260
261 doc->AddProperty(META_NS::ConstructProperty<IntfPtr>("RenderContext", nullptr, flags));
262 doc->AddProperty(META_NS::ConstructProperty<IntfPtr>("EngineQueue", nullptr, flags));
263 doc->AddProperty(META_NS::ConstructProperty<IntfPtr>("AppQueue", nullptr, flags));
264 doc->AddProperty(META_NS::ConstructArrayProperty<IntfWeakPtr>("Scenes", {}, flags));
265
266 doc->GetPropertyByName<META_NS::SharedPtrIInterface>("EngineQueue")->SetValue(engineThread);
267 doc->GetPropertyByName<META_NS::SharedPtrIInterface>("AppQueue")->SetValue(appThread);
268 doc->GetPropertyByName<META_NS::SharedPtrIInterface>("RenderContext")->SetValue(engineInstance_.renderContext_);
269
270 WIDGET_LOGD("register shader path");
271 static constexpr const RENDER_NS::IShaderManager::ShaderFilePathDesc desc { "shaders://" };
272 engineInstance_.engine_->GetFileManager().RegisterPath("shaders", "OhosRawFile://shaders", false);
273 engineInstance_.renderContext_->GetDevice().GetShaderManager().LoadShaderFiles(desc);
274
275 engineInstance_.engine_->GetFileManager().RegisterPath("appshaders", "OhosRawFile://shaders", false);
276 static constexpr const RENDER_NS::IShaderManager::ShaderFilePathDesc desc1 { "appshaders://" };
277 engineInstance_.renderContext_->GetDevice().GetShaderManager().LoadShaderFiles(desc1);
278
279 WIDGET_LOGD("init engine success");
280 return META_NS::IAny::Ptr {};
281 });
282
283 engineThread->AddWaitableTask(engineInit)->Wait();
284
285 return true;
286 }
287
SetSceneObj(META_NS::IObject::Ptr pt)288 void SceneAdapter::SetSceneObj(META_NS::IObject::Ptr pt)
289 {
290 WIDGET_LOGD("SceneAdapterImpl::SetSceneObj");
291 sceneWidgetObj_ = pt;
292 }
293
CreateTextureLayer()294 std::shared_ptr<TextureLayer> SceneAdapter::CreateTextureLayer()
295 {
296 InitRenderThread();
297 auto cb = META_NS::MakeCallback<META_NS::ITaskQueueWaitableTask>([this]() {
298 textureLayer_ = std::make_shared<TextureLayer>(key_);
299 key_++;
300 return META_NS::IAny::Ptr {};
301 });
302 META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD)->AddWaitableTask(cb)->Wait();
303 return textureLayer_;
304 }
305
LoadPluginsAndInit()306 bool SceneAdapter::LoadPluginsAndInit()
307 {
308 WIDGET_LOGD("scene adapter loadPlugins");
309 hapInfo_ = GetHapInfo();
310
311 #define TO_STRING(name) #name
312 #define PLATFORM_PATH_NAME(name) TO_STRING(name)
313 CORE_NS::PlatformCreateInfo platformCreateInfo {
314 PLATFORM_PATH_NAME(PLATFORM_CORE_ROOT_PATH),
315 PLATFORM_PATH_NAME(PLATFORM_APP_ROOT_PATH),
316 PLATFORM_PATH_NAME(PLATFORM_APP_PLUGIN_PATH),
317 hapInfo_.hapPath_.c_str(),
318 hapInfo_.bundleName_.c_str(),
319 hapInfo_.moduleName_.c_str()
320 };
321 #undef TO_STRING
322 #undef PLATFORM_PATH_NAME
323 if (!LoadPlugins(platformCreateInfo)) {
324 return false;
325 }
326
327 if (!InitEngine(platformCreateInfo)) {
328 return false;
329 }
330
331 return true;
332 }
333
OnWindowChange(const WindowChangeInfo & windowChangeInfo)334 void SceneAdapter::OnWindowChange(const WindowChangeInfo& windowChangeInfo)
335 {
336 WIDGET_LOGD("OnWindowchange");
337 auto cb = META_NS::MakeCallback<META_NS::ITaskQueueWaitableTask>([this, &windowChangeInfo]() {
338 textureLayer_->OnWindowChange(windowChangeInfo);
339 const auto& textureInfo = textureLayer_->GetTextureInfo();
340 auto& device = engineInstance_.renderContext_->GetDevice();
341 RENDER_NS::SwapchainCreateInfo swapchainCreateInfo {
342 // reinterpret_cast<uint64_t>(eglSurface_),
343 0U,
344 RENDER_NS::SwapchainFlagBits::CORE_SWAPCHAIN_COLOR_BUFFER_BIT |
345 RENDER_NS::SwapchainFlagBits::CORE_SWAPCHAIN_DEPTH_BUFFER_BIT |
346 RENDER_NS::SwapchainFlagBits::CORE_SWAPCHAIN_SRGB_BIT,
347 RENDER_NS::ImageUsageFlagBits::CORE_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
348 {
349 reinterpret_cast<uintptr_t>(textureInfo.nativeWindow_),
350 reinterpret_cast<uintptr_t>(static_cast<const RENDER_NS::DevicePlatformDataGLES&>(
351 device.GetPlatformData()).display)
352 }
353 };
354 swapchainHandle_ = device.CreateSwapchainHandle(swapchainCreateInfo, swapchainHandle_, {});
355 return META_NS::IAny::Ptr {};
356 });
357
358 META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD)->AddWaitableTask(cb)->Wait();
359 }
360
InitRenderThread()361 void SceneAdapter::InitRenderThread()
362 {
363 // Task used for oneshot renders
364 singleFrameAsync = META_NS::MakeCallback<META_NS::ITaskQueueTask>([this]() {
365 RenderFunction();
366 return 0;
367 });
368 // Task used for oneshot synchronous renders
369 singleFrameSync = META_NS::MakeCallback<META_NS::ITaskQueueWaitableTask>([this]() {
370 RenderFunction();
371 return META_NS::IAny::Ptr {};
372 });
373 }
374 // where to put this deinit
DeinitRenderThread()375 void SceneAdapter::DeinitRenderThread()
376 {
377 auto ioThread = META_NS::GetTaskQueueRegistry().GetTaskQueue(IO_QUEUE);
378 if (renderTask) {
379 engineThread->CancelTask(renderTask);
380 renderTask = nullptr;
381 }
382 auto engine_deinit = META_NS::MakeCallback<META_NS::ITaskQueueWaitableTask>([]() {
383 // destroy all swapchains
384 auto &obr = META_NS::GetObjectRegistry();
385 auto doc = interface_cast<META_NS::IMetadata>(obr.GetDefaultObjectContext());
386
387 // hmm.. this is somewhat uncool
388 {
389 auto p1 = doc->GetPropertyByName<IntfPtr>("EngineQueue");
390 doc->RemoveProperty(p1);
391 auto p2 = doc->GetPropertyByName<IntfPtr>("AppQueue");
392 doc->RemoveProperty(p2);
393 auto p3 = doc->GetPropertyByName<IntfPtr>("RenderContext");
394 doc->RemoveProperty(p3);
395 }
396
397 doc->GetArrayPropertyByName<IntfWeakPtr>("Scenes")->Reset();
398 engineInstance_.renderContext_.reset();
399 engineInstance_.engine_.reset();
400
401 return META_NS::IAny::Ptr{};
402 });
403 engineThread->AddWaitableTask(engine_deinit)->Wait();
404 singleFrameAsync.reset();
405 auto &tr = META_NS::GetTaskQueueRegistry();
406 tr.UnregisterTaskQueue(ENGINE_THREAD);
407 engineThread.reset();
408 tr.UnregisterTaskQueue(IO_QUEUE);
409 ioThread.reset();
410 }
411
RenderFunction()412 void SceneAdapter::RenderFunction()
413 {
414 WIDGET_SCOPED_TRACE("SceneAdapter::RenderFunction");
415 auto &obr = META_NS::GetObjectRegistry();
416 auto doc = interface_cast<META_NS::IMetadata>(obr.GetDefaultObjectContext());
417 BASE_NS::shared_ptr<RENDER_NS::IRenderContext> rc;
418 if (auto prp = doc->GetPropertyByName<IntfPtr>("RenderContext")) {
419 rc = interface_pointer_cast<RENDER_NS::IRenderContext>(prp->GetValue());
420 }
421 LockCompositor();
422 auto scene = interface_pointer_cast<SCENE_NS::IScene>(sceneWidgetObj_);
423 if (!bitmap_) {
424 auto cams = scene->GetCameras();
425 if (!cams.empty()) {
426 for (auto c : cams) {
427 AttachSwapchain(interface_pointer_cast<META_NS::IObject>(c), swapchainHandle_);
428 }
429 }
430 }
431 scene->RenderCameras();
432 rc->GetRenderer().RenderDeferredFrame();
433 UnlockCompositor();
434 }
435
RenderFrame(bool needsSyncPaint)436 void SceneAdapter::RenderFrame(bool needsSyncPaint)
437 {
438 if (renderTask) {
439 engineThread->CancelTask(renderTask);
440 renderTask = nullptr;
441 }
442
443 if (!needsSyncPaint) {
444 renderTask = engineThread->AddTask(singleFrameAsync);
445 } else {
446 engineThread->AddWaitableTask(singleFrameSync)->Wait();
447 }
448 }
449
NeedsRepaint()450 bool SceneAdapter::NeedsRepaint()
451 {
452 return needsRepaint_;
453 }
454
AttachSwapchain(META_NS::IObject::Ptr cameraObj,RENDER_NS::RenderHandleReference swapchain)455 void SceneAdapter::AttachSwapchain(META_NS::IObject::Ptr cameraObj, RENDER_NS::RenderHandleReference swapchain)
456 {
457 WIDGET_LOGD("attach swapchain");
458 auto scene = interface_cast<SCENE_NS::INode>(cameraObj)->GetScene();
459 auto camera = interface_pointer_cast<SCENE_NS::ICamera>(cameraObj);
460 if (scene->IsCameraActive(camera)) {
461 auto externalBitmapUid = BASE_NS::Uid("77b38a92-8182-4562-bd3f-deab7b40cedc");
462 bitmap_ = META_NS::GetObjectRegistry().Create<SCENE_NS::IBitmap>(externalBitmapUid);
463
464 scene->SetBitmap(bitmap_, camera);
465 const auto &info = textureLayer_->GetTextureInfo();
466
467 bitmap_->SetRenderHandle(swapchainHandle_,
468 { static_cast<uint32_t>(info.width_ * info.widthScale_),
469 static_cast<uint32_t>(info.height_ * info.heightScale_) });
470 }
471 }
472 } // namespace OHOS::Render3D
473