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