• 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 "RenderContextJS.h"
17 
18 #include <base/util/uid.h>
19 
20 #include <meta/interface/intf_metadata.h>
21 #include <meta/interface/intf_object_context.h>
22 
23 #include <scene/interface/intf_shader.h>
24 #include <scene/interface/intf_render_context.h>
25 #include <render/device/intf_gpu_resource_manager.h>
26 
27 
28 #include <core/image/intf_image_loader_manager.h>
29 #include <core/intf_engine.h>
30 
31 #include <render/device/intf_gpu_resource_manager.h>
32 
33 #include "JsObjectCache.h"
34 #include "ParamParsing.h"
35 #include "Promise.h"
36 #include "nodejstaskqueue.h"
37 
38 static constexpr BASE_NS::Uid IO_QUEUE { "be88e9a0-9cd8-45ab-be48-937953dc258f" };
39 
40 META_TYPE(BASE_NS::shared_ptr<CORE_NS::IImageLoaderManager::LoadResult>);
41 
42 struct GlobalResources {
43     NapiApi::WeakRef defaultContext;
44 };
45 
46 static BASE_NS::weak_ptr<GlobalResources> globalResources;
47 
RenderResources(napi_env env)48 RenderResources::RenderResources(napi_env env) : env_(env)
49 {
50     // Acquire the js task queue. (make sure that as long as we have a scene, the nodejstaskqueue is useable)
51     if (auto jsQueue = interface_cast<INodeJSTaskQueue>(GetOrCreateNodeTaskQueue(env))) {
52         jsQueue->Acquire();
53     }
54 }
55 
~RenderResources()56 RenderResources::~RenderResources()
57 {
58     disposeContainer_.DisposeAll(env_);
59 
60     for (auto b : bitmaps_) {
61         b.second.reset();
62     }
63 
64     // Release the js task queue.
65     auto tq = META_NS::GetTaskQueueRegistry().GetTaskQueue(JS_THREAD_DEP);
66     if (auto p = interface_cast<INodeJSTaskQueue>(tq)) {
67         p->Release();
68         // check if we can safely de-init here also.
69         if (p->IsReleased()) {
70             // destroy and unregister the queue.
71             DeinitNodeTaskQueue();
72         }
73     }
74 }
75 
DisposeHook(uintptr_t token,NapiApi::Object obj)76 void RenderResources::DisposeHook(uintptr_t token, NapiApi::Object obj)
77 {
78     disposeContainer_.DisposeHook(token, obj);
79 }
80 
ReleaseDispose(uintptr_t token)81 void RenderResources::ReleaseDispose(uintptr_t token)
82 {
83     disposeContainer_.ReleaseDispose(token);
84 }
85 
StrongDisposeHook(uintptr_t token,NapiApi::Object obj)86 void RenderResources::StrongDisposeHook(uintptr_t token, NapiApi::Object obj)
87 {
88     disposeContainer_.StrongDisposeHook(token, obj);
89 }
90 
ReleaseStrongDispose(uintptr_t token)91 void RenderResources::ReleaseStrongDispose(uintptr_t token)
92 {
93     disposeContainer_.ReleaseStrongDispose(token);
94 }
95 
StoreBitmap(BASE_NS::string_view uri,SCENE_NS::IBitmap::Ptr bitmap)96 void RenderResources::StoreBitmap(BASE_NS::string_view uri, SCENE_NS::IBitmap::Ptr bitmap)
97 {
98     CORE_NS::UniqueLock lock(mutex_);
99     if (bitmap) {
100         bitmaps_[uri] = bitmap;
101     } else {
102         // setting null. releases.
103         bitmaps_.erase(uri);
104     }
105 }
106 
FetchBitmap(BASE_NS::string_view uri)107 SCENE_NS::IBitmap::Ptr RenderResources::FetchBitmap(BASE_NS::string_view uri)
108 {
109     CORE_NS::UniqueLock lock(mutex_);
110     auto it = bitmaps_.find(uri);
111     if (it != bitmaps_.end()) {
112         return it->second;
113     }
114     return {};
115 }
116 
Init(napi_env env,napi_value exports)117 void RenderContextJS::Init(napi_env env, napi_value exports)
118 {
119     using namespace NapiApi;
120     napi_property_descriptor props[] = {
121         // static methods
122         Method<NapiApi::FunctionContext<>, RenderContextJS, &RenderContextJS::GetResourceFactory>(
123             "getRenderResourceFactory"),
124         Method<NapiApi::FunctionContext<>, RenderContextJS, &RenderContextJS::Dispose>("destroy"),
125 
126         // SceneResourceFactory methods
127         Method<NapiApi::FunctionContext<BASE_NS::string>, RenderContextJS, &RenderContextJS::LoadPlugin>("loadPlugin"),
128         Method<NapiApi::FunctionContext<NapiApi::Object>, RenderContextJS, &RenderContextJS::CreateShader>(
129             "createShader"),
130         Method<NapiApi::FunctionContext<NapiApi::Object>, RenderContextJS, &RenderContextJS::CreateImage>(
131             "createImage"),
132         Method<NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object>, RenderContextJS,
133                &RenderContextJS::CreateMeshResource>("createMesh"),
134         Method<NapiApi::FunctionContext<BASE_NS::string, BASE_NS::string>, RenderContextJS,
135                &RenderContextJS::RegisterResourcePath>("registerResourcePath"),
136     };
137 
138     napi_value func;
139     auto status = napi_define_class(env, "RenderContext", NAPI_AUTO_LENGTH, BaseObject::ctor<RenderContextJS>(),
140         nullptr, sizeof(props) / sizeof(props[0]), props, &func);
141 
142     napi_set_named_property(env, exports, "RenderContext", func);
143 
144     NapiApi::MyInstanceState* mis;
145     NapiApi::MyInstanceState::GetInstance(env, reinterpret_cast<void**>(&mis));
146     if (mis) {
147         mis->StoreCtor("RenderContext", func);
148     }
149 }
150 
RegisterEnums(NapiApi::Object exports)151 void RenderContextJS::RegisterEnums(NapiApi::Object exports) {}
152 
RenderContextJS(napi_env env,napi_callback_info info)153 RenderContextJS::RenderContextJS(napi_env env, napi_callback_info info)
154     : BaseObject(env, info), env_(env), globalResources_(globalResources.lock())
155 {
156     LOG_V("RenderContextJS ++");
157 }
158 
~RenderContextJS()159 RenderContextJS::~RenderContextJS()
160 {
161     LOG_V("RenderContextJS --");
162 }
163 
GetInstanceImpl(uint32_t id)164 void* RenderContextJS::GetInstanceImpl(uint32_t id)
165 {
166     if (id == RenderContextJS::ID) {
167         return this;
168     }
169 
170     return nullptr;
171 }
172 
GetResources() const173 BASE_NS::shared_ptr<RenderResources> RenderContextJS::GetResources() const
174 {
175     if (auto resources = resources_.lock()) {
176         return resources;
177     }
178 
179     BASE_NS::shared_ptr<RenderResources> resources(new RenderResources(env_));
180     resources_ = resources;
181 
182     return resources;
183 }
184 
GetDefaultContext(napi_env env)185 napi_value RenderContextJS::GetDefaultContext(napi_env env)
186 {
187     auto resources = globalResources.lock();
188     if (!resources) {
189         resources.reset(new GlobalResources);
190         globalResources = resources;
191     }
192 
193     NapiApi::Object context = NapiApi::Object(env, "RenderContext", {});
194     resources->defaultContext = context;
195 
196     return context.ToNapiValue();
197 }
198 
Dispose(NapiApi::FunctionContext<> & ctx)199 napi_value RenderContextJS::Dispose(NapiApi::FunctionContext<>& ctx)
200 {
201     return {};
202 }
203 
DisposeNative(void *)204 void RenderContextJS::DisposeNative(void*) {}
205 
Finalize(napi_env env)206 void RenderContextJS::Finalize(napi_env env) {}
207 
GetResourceFactory(NapiApi::FunctionContext<> & ctx)208 napi_value RenderContextJS::GetResourceFactory(NapiApi::FunctionContext<>& ctx)
209 {
210     return ctx.This().ToNapiValue();
211 }
212 
LoadPlugin(NapiApi::FunctionContext<BASE_NS::string> & ctx)213 napi_value RenderContextJS::LoadPlugin(NapiApi::FunctionContext<BASE_NS::string>& ctx)
214 {
215     auto c = ctx.Arg<0>().valueOrDefault();
216     if (!BASE_NS::IsUidString(c)) {
217         LOG_E("\"%s\" is not a Uid string", c.c_str());
218 
219         Promise promise(ctx.GetEnv());
220         NapiApi::Value<bool> result(ctx.GetEnv(), false);
221         promise.Resolve(result.ToNapiValue());
222 
223         return promise.ToNapiValue();
224     }
225 
226     LOG_E("Loading plugin: %s", c.c_str());
227 
228     BASE_NS::Uid uid(*(char(*)[37])c.data());
229 
230     const auto engineQ = META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD);
231     META_NS::AddFutureTaskOrRunDirectly(engineQ, [uid]() {
232         Core::GetPluginRegister().LoadPlugins({uid});
233     });
234 
235     Promise promise(ctx.GetEnv());
236     NapiApi::Value<bool> result(ctx.GetEnv(), true);
237     promise.Resolve(result.ToNapiValue());
238 
239     return promise.ToNapiValue();
240 }
241 
CreateShader(NapiApi::FunctionContext<NapiApi::Object> & ctx)242 napi_value RenderContextJS::CreateShader(NapiApi::FunctionContext<NapiApi::Object>& ctx)
243 {
244     return ctx.GetUndefined();
245 }
246 
CreateImage(NapiApi::FunctionContext<NapiApi::Object> & ctx)247 napi_value RenderContextJS::CreateImage(NapiApi::FunctionContext<NapiApi::Object>& ctx)
248 {
249     // Create an image in four steps:
250     // 1. Parse args in JS thread (this function body)
251     // 2. Load image data in IO thread
252     // 3. Create GPU resource in engine thread
253     // 4. Settle promise by converting to JS object in JS release thread
254     using namespace RENDER_NS;
255     const auto env = ctx.GetEnv();
256     auto promise = Promise(env);
257 
258     NapiApi::Object resourceParams = ctx.Arg<0>();
259     auto uri = ExtractUri(resourceParams.Get<NapiApi::Object>("uri"));
260     if (uri.empty()) {
261         auto u = resourceParams.Get<BASE_NS::string>("uri");
262         uri = ExtractUri(u);
263     }
264 
265     if (uri.empty()) {
266         return promise.Reject("Invalid scene resource Image parameters given");
267     }
268 
269     if (auto resources = resources_.lock()) {
270         if (const auto bitmap = resources->FetchBitmap(uri)) {
271             // no aliasing.. so the returned bitmaps name is.. the old one.
272             const auto result = FetchJsObj(bitmap).ToNapiValue();
273             return promise.Resolve(result);
274         }
275     }
276 
277     auto& obr = META_NS::GetObjectRegistry();
278     auto doc = interface_cast<META_NS::IMetadata>(obr.GetDefaultObjectContext());
279     auto renderContext = doc->GetProperty<SCENE_NS::IRenderContext::Ptr>("RenderContext")->GetValue()->GetRenderer();
280 
281     using LoadResult = CORE_NS::IImageLoaderManager::LoadResult;
282     auto loadImage = [uri, renderContext]() {
283         uint32_t imageLoaderFlags = CORE_NS::IImageLoaderManager::IMAGE_LOADER_GENERATE_MIPS;
284         auto& imageLoaderMgr = renderContext->GetEngine().GetImageLoaderManager();
285         // LoadResult contains a unique pointer, so can't copy. Move it to the heap and pass a pointer instead.
286         return BASE_NS::shared_ptr<LoadResult> { new LoadResult { imageLoaderMgr.LoadImage(uri, imageLoaderFlags) } };
287     };
288 
289     auto createGpuResource = [uri, renderContext](
290                                  BASE_NS::shared_ptr<LoadResult> loadResult) -> SCENE_NS::IBitmap::Ptr {
291         if (!loadResult->success) {
292             LOG_E("%s", BASE_NS::string { "Failed to load image: " }.append(loadResult->error).c_str());
293             return {};
294         }
295 
296         auto& gpuResourceMgr = renderContext->GetDevice().GetGpuResourceManager();
297         GpuImageDesc gpuDesc = gpuResourceMgr.CreateGpuImageDesc(loadResult->image->GetImageDesc());
298         gpuDesc.usageFlags = CORE_IMAGE_USAGE_SAMPLED_BIT | CORE_IMAGE_USAGE_TRANSFER_DST_BIT;
299         if (gpuDesc.engineCreationFlags & EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_GENERATE_MIPS) {
300             gpuDesc.usageFlags |= CORE_IMAGE_USAGE_TRANSFER_SRC_BIT;
301         }
302         gpuDesc.memoryPropertyFlags = CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
303         const auto imageHandle = gpuResourceMgr.Create(uri, gpuDesc, std::move(loadResult->image));
304 
305         auto& obr = META_NS::GetObjectRegistry();
306         auto doc = interface_pointer_cast<META_NS::IMetadata>(obr.GetDefaultObjectContext());
307         auto bitmap = META_NS::GetObjectRegistry().Create<SCENE_NS::IBitmap>(SCENE_NS::ClassId::Bitmap, doc);
308         if (auto m = interface_cast<META_NS::IMetadata>(bitmap)) {
309             m->AddProperty(META_NS::ConstructProperty<BASE_NS::string>("Uri", uri));
310         }
311         if (auto i = interface_cast<SCENE_NS::IRenderResource>(bitmap)) {
312             i->SetRenderHandle(imageHandle);
313         }
314         return bitmap;
315     };
316 
317     auto convertToJs = [promise, uri, contextRef = NapiApi::StrongRef(ctx.This()), resources = GetResources(),
318                            paramRef = NapiApi::StrongRef(resourceParams)](SCENE_NS::IBitmap::Ptr bitmap) mutable {
319         if (!bitmap) {
320             promise.Reject(BASE_NS::string { "Failed to load image from URI " }.append(uri));
321             return;
322         }
323         const auto env = promise.Env();
324         napi_value args[] = { contextRef.GetValue(), paramRef.GetValue() };
325         const auto result = CreateFromNativeInstance(env, bitmap, PtrType::WEAK, args);
326 
327         auto renderContextJs = contextRef.GetObject().GetJsWrapper<RenderContextJS>();
328         resources->StoreBitmap(uri, BASE_NS::move(bitmap));
329 
330         promise.Resolve(result.ToNapiValue());
331     };
332 
333     const auto ioQ = META_NS::GetTaskQueueRegistry().GetTaskQueue(IO_QUEUE);
334     const auto engineQ = META_NS::GetTaskQueueRegistry().GetTaskQueue(ENGINE_THREAD);
335     const auto jsQ = META_NS::GetTaskQueueRegistry().GetTaskQueue(JS_THREAD_DEP);
336     META_NS::AddFutureTaskOrRunDirectly(ioQ, BASE_NS::move(loadImage))
337         .Then(BASE_NS::move(createGpuResource), engineQ)
338         .Then(BASE_NS::move(convertToJs), jsQ);
339 
340     return promise;
341 }
342 
CreateMeshResource(NapiApi::FunctionContext<NapiApi::Object,NapiApi::Object> & ctx)343 napi_value RenderContextJS::CreateMeshResource(NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object>& ctx)
344 {
345     return ctx.GetUndefined();
346 }
347 
RegisterResourcePath(NapiApi::FunctionContext<BASE_NS::string,BASE_NS::string> & ctx)348 napi_value RenderContextJS::RegisterResourcePath(NapiApi::FunctionContext<BASE_NS::string, BASE_NS::string>& ctx)
349 {
350     using namespace RENDER_NS;
351 
352     // 1.read current path of shader and protocol name
353     auto registerProtocol = ctx.Arg<0>().valueOrDefault();
354     auto resourcePath = ctx.Arg<1>().valueOrDefault();
355     LOG_E("register resource path is: [%s], register protocol is : [%s]",
356         resourcePath.c_str(), registerProtocol.c_str());
357 
358     // 2.Check Empty for path & protocol
359     if (resourcePath.empty() || registerProtocol.empty()) {
360         LOG_E("Invalid register path or protocol of assets given");
361         return ctx.GetBoolean(false);
362     }
363 
364     auto& obr = META_NS::GetObjectRegistry();
365     auto doc = interface_cast<META_NS::IMetadata>(obr.GetDefaultObjectContext());
366     auto& fileManager = doc->GetProperty<SCENE_NS::IRenderContext::Ptr>("RenderContext")
367                             ->GetValue()->GetRenderer()->GetEngine().GetFileManager();
368 
369     // 3.Check if the proxy protocol exists already.
370     if (!(fileManager.CheckExistence(registerProtocol))) {
371         LOG_E("Register protocol exists already");
372         return ctx.GetBoolean(false);
373     }
374     // 4.Check if the protocol is already declared. | Register now!
375     if (!(fileManager.RegisterPath(registerProtocol.c_str(), resourcePath.c_str(), false))) {
376         LOG_E("Register protocol declared already");
377         return ctx.GetBoolean(false);
378     }
379 
380     return ctx.GetBoolean(true);
381 }