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 "PostProcJS.h"
17 
18 #include <meta/api/make_callback.h>
19 #include <meta/interface/intf_task_queue.h>
20 #include <meta/interface/intf_task_queue_registry.h>
21 #include <meta/interface/property/property_events.h>
22 #include <scene_plugin/api/camera.h> //for the classid...
23 #include <scene_plugin/api/node_uid.h>
24 #include <scene_plugin/interface/intf_ecs_scene.h>
25 #include <scene_plugin/interface/intf_node.h>
26 #include <scene_plugin/interface/intf_scene.h>
27 
28 #include <render/intf_render_context.h>
29 
30 #include "CameraJS.h"
31 using IntfPtr = BASE_NS::shared_ptr<CORE_NS::IInterface>;
32 using IntfWeakPtr = BASE_NS::weak_ptr<CORE_NS::IInterface>;
33 using namespace SCENE_NS;
34 
Init(napi_env env,napi_value exports)35 void PostProcJS::Init(napi_env env, napi_value exports)
36 {
37     using namespace NapiApi;
38 
39     BASE_NS::vector<napi_property_descriptor> node_props;
40     // clang-format off
41 
42     node_props.push_back(GetSetProperty<bool, PostProcJS, &PostProcJS::GetBloom, &PostProcJS::SetBloom>("bloom"));
43     node_props.emplace_back(GetSetProperty<Object, PostProcJS, &PostProcJS::GetToneMapping,
44         &PostProcJS::SetToneMapping>("toneMapping"));
45     node_props.push_back(MakeTROMethod<NapiApi::FunctionContext<>, PostProcJS, &PostProcJS::Dispose>("destroy"));
46 
47     // clang-format on
48 
49     napi_value func;
50     auto status = napi_define_class(env, "PostProcessSettings", NAPI_AUTO_LENGTH, BaseObject::ctor<PostProcJS>(),
51         nullptr, node_props.size(), node_props.data(), &func);
52 
53     NapiApi::MyInstanceState* mis;
54     napi_get_instance_data(env, (void**)&mis);
55     mis->StoreCtor("PostProcessSettings", func);
56 }
57 
Dispose(NapiApi::FunctionContext<> & ctx)58 napi_value PostProcJS::Dispose(NapiApi::FunctionContext<>& ctx)
59 {
60     LOG_F("PostProcJS::Dispose");
61     DisposeNative();
62     return {};
63 }
DisposeNative()64 void PostProcJS::DisposeNative()
65 {
66     if (!disposed_) {
67         disposed_ = true;
68         LOG_F("PostProcJS::DisposeNative");
69         // make sure we release toneMap settings
70 
71         auto tmjs = toneMap_.GetObject();
72         if (tmjs) {
73             NapiApi::Function func = tmjs.Get<NapiApi::Function>("destroy");
74             if (func) {
75                 func.Invoke(tmjs);
76             }
77         }
78         toneMap_.Reset();
79 
80         if (auto post = interface_pointer_cast<IPostProcess>(GetNativeObject())) {
81             // reset the native object refs
82             SetNativeObject(nullptr, false);
83             SetNativeObject(nullptr, true);
84 
85             auto cameraJS = camera_.GetObject();
86             if (cameraJS) {
87                 auto* rootobject = cameraJS.Native<TrueRootObject>();
88                 CameraJS* cam = (CameraJS*)(rootobject);
89 
90                 ExecSyncTask([cam, post = BASE_NS::move(post)]() {
91                     cam->ReleaseObject(META_NS::interface_pointer_cast<META_NS::IObject>(post));
92                     post->Tonemap()->SetValue(nullptr);
93                     return META_NS::IAny::Ptr {};
94                 });
95             }
96         }
97     }
98 }
GetInstanceImpl(uint32_t id)99 void* PostProcJS::GetInstanceImpl(uint32_t id)
100 {
101     if (id == PostProcJS::ID) {
102         return this;
103     }
104     return nullptr;
105 }
Finalize(napi_env env)106 void PostProcJS::Finalize(napi_env env)
107 {
108     // need to do something BEFORE the object gets deleted..
109     DisposeNative();
110     BaseObject<PostProcJS>::Finalize(env);
111 }
112 
PostProcJS(napi_env e,napi_callback_info i)113 PostProcJS::PostProcJS(napi_env e, napi_callback_info i) : BaseObject<PostProcJS>(e, i)
114 {
115     LOG_F("PostProcJS ++");
116     NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object> fromJs(e, i);
117     if (!fromJs) {
118         // no arguments. so internal create.
119         // expecting caller to finish
120         return;
121     }
122 
123     // camera that we bind to..
124     NapiApi::Object cameraJS = fromJs.Arg<0>();
125     camera_ = { cameraJS };
126     auto* rootobject = cameraJS.Native<TrueRootObject>();
127     auto postproc = interface_pointer_cast<SCENE_NS::IPostProcess>(
128         ((CameraJS*)(rootobject))->CreateObject(SCENE_NS::ClassId::PostProcess));
129 
130     // create a postprocess object owned by CameraJS.
131 
132     // process constructor args..
133     NapiApi::Object meJs(e, fromJs.This());
134     // weak ref, as we expect to be owned by the camera.
135     SetNativeObject(interface_pointer_cast<META_NS::IObject>(postproc), false);
136     StoreJsObj(interface_pointer_cast<META_NS::IObject>(postproc), meJs);
137     // now, based on parameters, create correct objects.
138     if (NapiApi::Object args = fromJs.Arg<1>()) {
139         if (auto prm = args.Get("toneMapping")) {
140             // enable tonemap.
141             napi_value args[] = {
142                 meJs, // postprocess
143                 prm   // tonemap settings
144             };
145             SCENE_NS::ITonemap::Ptr tone;
146             ExecSyncTask([postproc, &tone]() {
147                 tone = postproc->Tonemap()->GetValue();
148                 return META_NS::IAny::Ptr {};
149             });
150             MakeNativeObjectParam(e, tone, BASE_NS::countof(args), args);
151             NapiApi::Object tonemapJS(GetJSConstructor(e, "ToneMappingSettings"), BASE_NS::countof(args), args);
152             meJs.Set("toneMapping", tonemapJS);
153         }
154         if (auto prm = args.Get("bloom")) {
155             ExecSyncTask([postproc]() -> META_NS::IAny::Ptr {
156                 SCENE_NS::IBloom::Ptr bloom = postproc->Bloom()->GetValue();
157                 bloom->Enabled()->SetValue(true);
158                 return {};
159             });
160         }
161     }
162 }
163 
~PostProcJS()164 PostProcJS::~PostProcJS()
165 {
166     LOG_F("PostProcJS --");
167     DisposeNative();
168     if (!GetNativeObject()) {
169         return;
170     }
171 }
172 
SetToneMapping(NapiApi::FunctionContext<NapiApi::Object> & ctx)173 void PostProcJS::SetToneMapping(NapiApi::FunctionContext<NapiApi::Object>& ctx)
174 {
175     auto postproc = interface_cast<SCENE_NS::IPostProcess>(GetNativeObject());
176     if (!postproc) {
177         // not possible.
178         return;
179     }
180     NapiApi::Object tonemapJS = ctx.Arg<0>();
181 
182     if (auto currentlySet = toneMap_.GetObject()) {
183         if ((napi_value)currentlySet == (napi_value)tonemapJS) {
184             // setting the exactly the same tonemap setting. do nothing.
185             return;
186         }
187         // dispose the old bound object..
188         NapiApi::Function func = currentlySet.Get<NapiApi::Function>("destroy");
189         if (func) {
190             func.Invoke(currentlySet);
191         }
192         toneMap_.Reset();
193     }
194 
195     TrueRootObject* native { nullptr };
196     SCENE_NS::ITonemap::Ptr tonemap;
197     // does the input parameter already have a bridge..
198     native = tonemapJS.Native<TrueRootObject>();
199     if (!native) {
200         // nope.. so create a new bridge object based on the input.
201         napi_value args[] = {
202             ctx.This(), // postproc..
203             ctx.Arg<0>() // "javascript object for values"
204         };
205         NapiApi::Object res(GetJSConstructor(ctx, "ToneMappingSettings"), BASE_NS::countof(args), args);
206         native = res.Native<TrueRootObject>();
207         tonemapJS = res;
208     } else {
209         tonemap = interface_pointer_cast<SCENE_NS::ITonemap>(native->GetNativeObject());
210         ExecSyncTask([postproc, tonemap]() {
211             postproc->Tonemap()->SetValue(tonemap);
212             return META_NS::IAny::Ptr {};
213         });
214     }
215     toneMap_ = { ctx, tonemapJS }; // take ownership of the object.
216 }
217 
GetToneMapping(NapiApi::FunctionContext<> & ctx)218 napi_value PostProcJS::GetToneMapping(NapiApi::FunctionContext<>& ctx)
219 {
220     if (auto postproc = interface_cast<SCENE_NS::IPostProcess>(GetNativeObject())) {
221         SCENE_NS::ITonemap::Ptr tone;
222         ExecSyncTask([postproc, &tone]() {
223             tone = postproc->Tonemap()->GetValue();
224             return META_NS::IAny::Ptr {};
225         });
226         auto obj = interface_pointer_cast<META_NS::IObject>(tone);
227 
228         if (auto cached = FetchJsObj(obj)) {
229             // always return the same js object.
230             return cached;
231         }
232 
233         napi_value args[] = {
234             ctx.This() // postproc..
235         };
236         MakeNativeObjectParam(ctx, tone, BASE_NS::countof(args), args);
237         napi_value tonemapJS = CreateFromNativeInstance(ctx, obj, false, BASE_NS::countof(args), args);
238         toneMap_ = { ctx, tonemapJS }; // take ownership of the object.
239         return tonemapJS;
240     }
241     toneMap_.Reset();
242     return ctx.GetUndefined();
243 }
244 
GetBloom(NapiApi::FunctionContext<> & ctx)245 napi_value PostProcJS::GetBloom(NapiApi::FunctionContext<>& ctx)
246 {
247     bool enabled = false;
248     if (auto postproc = interface_pointer_cast<SCENE_NS::IPostProcess>(GetNativeObject())) {
249         ExecSyncTask([postproc, &enabled]() {
250             SCENE_NS::IBloom::Ptr bloom = postproc->Bloom()->GetValue();
251             enabled = bloom->Enabled()->GetValue();
252             return META_NS::IAny::Ptr {};
253         });
254     }
255 
256     napi_value value;
257     napi_status status = napi_get_boolean(ctx, enabled, &value);
258     return value;
259 }
260 
SetBloom(NapiApi::FunctionContext<bool> & ctx)261 void PostProcJS::SetBloom(NapiApi::FunctionContext<bool>& ctx)
262 {
263     bool enable = ctx.Arg<0>();
264     if (auto postproc = interface_pointer_cast<SCENE_NS::IPostProcess>(GetNativeObject())) {
265         ExecSyncTask([postproc, enable]() {
266             SCENE_NS::IBloom::Ptr bloom = postproc->Bloom()->GetValue();
267             bloom->Enabled()->SetValue(enable);
268             return META_NS::IAny::Ptr {};
269         });
270     }
271 }
272