• 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 "PostProcJS.h"
17 
18 #include <meta/api/make_callback.h>
19 #include <meta/api/util.h>
20 #include <meta/interface/intf_task_queue.h>
21 #include <meta/interface/intf_task_queue_registry.h>
22 #include <meta/interface/property/property_events.h>
23 #include <scene/interface/intf_node.h>
24 #include <scene/interface/intf_scene.h>
25 
26 #include <render/intf_render_context.h>
27 
28 #include "BloomJS.h"
29 #include "CameraJS.h"
30 using IntfPtr = BASE_NS::shared_ptr<CORE_NS::IInterface>;
31 using IntfWeakPtr = BASE_NS::weak_ptr<CORE_NS::IInterface>;
32 using namespace SCENE_NS;
33 
Init(napi_env env,napi_value exports)34 void PostProcJS::Init(napi_env env, napi_value exports)
35 {
36     using namespace NapiApi;
37 
38     BASE_NS::vector<napi_property_descriptor> node_props;
39     // clang-format off
40 
41     node_props.push_back(GetSetProperty<Object, PostProcJS, &PostProcJS::GetBloom, &PostProcJS::SetBloom>("bloom"));
42     node_props.emplace_back(
43         GetSetProperty<Object, PostProcJS, &PostProcJS::GetToneMapping, &PostProcJS::SetToneMapping>("toneMapping"));
44     node_props.push_back(MakeTROMethod<NapiApi::FunctionContext<>, PostProcJS, &PostProcJS::Dispose>("destroy"));
45 
46     // clang-format on
47 
48     napi_value func;
49     auto status = napi_define_class(env, "PostProcessSettings", NAPI_AUTO_LENGTH, BaseObject::ctor<PostProcJS>(),
50         nullptr, node_props.size(), node_props.data(), &func);
51 
52     NapiApi::MyInstanceState* mis;
53     GetInstanceData(env, (void**)&mis);
54     mis->StoreCtor("PostProcessSettings", func);
55 }
56 
Dispose(NapiApi::FunctionContext<> & ctx)57 napi_value PostProcJS::Dispose(NapiApi::FunctionContext<>& ctx)
58 {
59     LOG_V("PostProcJS::Dispose");
60     // see if we have "scenejs" as ext (prefer one given as argument)
61     napi_status stat;
62     CameraJS* ptr { nullptr };
63     NapiApi::FunctionContext<NapiApi::Object> args(ctx);
64     if (args) {
65         if (NapiApi::Object obj = args.Arg<0>()) {
66             if (napi_value ext = obj.Get("CameraJS")) {
67                 stat = napi_get_value_external(ctx.GetEnv(), ext, (void**)&ptr);
68             }
69         }
70     }
71     if (!ptr) {
72         NapiApi::Object obj = camera_.GetObject();
73         if (obj) {
74             auto* tro = obj.Native<TrueRootObject>();
75             if (tro) {
76                 ptr = static_cast<CameraJS*>(tro->GetInstanceImpl(CameraJS::ID));
77             }
78         }
79     }
80 
81     DisposeNative(ptr);
82     return {};
83 }
DisposeNative(void * cam)84 void PostProcJS::DisposeNative(void* cam)
85 {
86     if (cam == nullptr) {
87         if (!disposed_) {
88             LOG_F("PostProcJS::DisposeNative but argument NULL");
89         }
90         return;
91     }
92     if (!disposed_) {
93         disposed_ = true;
94         LOG_V("PostProcJS::DisposeNative");
95         // make sure we release toneMap settings
96 
97         if (auto tmjs = toneMap_.GetObject()) {
98             NapiApi::Function func = tmjs.Get<NapiApi::Function>("destroy");
99             if (func) {
100                 func.Invoke(tmjs);
101             }
102         }
103         toneMap_.Reset();
104 
105         if (auto bmjs = bloom_.GetObject()) {
106             NapiApi::Function func = bmjs.Get<NapiApi::Function>("destroy");
107             if (func) {
108                 func.Invoke(bmjs);
109             }
110         }
111         bloom_.Reset();
112         if (auto post = interface_pointer_cast<IPostProcess>(GetNativeObject())) {
113             // reset the native object refs
114             SetNativeObject(nullptr, false);
115             SetNativeObject(nullptr, true);
116             post->Tonemap()->SetValue(nullptr);
117             auto cameraJS = camera_.GetObject();
118             if (cameraJS) {
119                 auto* rootobject = cameraJS.Native<TrueRootObject>();
120                 CameraJS* cam = static_cast<CameraJS*>(rootobject);
121 
122                 ExecSyncTask([cam, post = BASE_NS::move(post)]() {
123                     cam->ReleaseObject(interface_pointer_cast<META_NS::IObject>(post));
124                     return META_NS::IAny::Ptr {};
125                 });
126             }
127         }
128     }
129 }
GetInstanceImpl(uint32_t id)130 void* PostProcJS::GetInstanceImpl(uint32_t id)
131 {
132     if (id == PostProcJS::ID)
133         return this;
134     return nullptr;
135 }
Finalize(napi_env env)136 void PostProcJS::Finalize(napi_env env)
137 {
138     // hmm.. do i need to do something BEFORE the object gets deleted..
139     DisposeNative(nullptr);
140     BaseObject<PostProcJS>::Finalize(env);
141 }
142 
PostProcJS(napi_env e,napi_callback_info i)143 PostProcJS::PostProcJS(napi_env e, napi_callback_info i) : BaseObject<PostProcJS>(e, i)
144 {
145     LOG_V("PostProcJS ++");
146     NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object> fromJs(e, i);
147     if (!fromJs) {
148         // no arguments. so internal create.
149         // expecting caller to finish
150         return;
151     }
152 
153     // camera that we bind to..
154     NapiApi::Object cameraJS = fromJs.Arg<0>();
155     camera_ = { cameraJS };
156     auto* rootobject = cameraJS.Native<TrueRootObject>();
157     if (rootobject == nullptr) {
158         LOG_E("rootobject is nullptr");
159         return;
160     }
161     auto postproc = interface_pointer_cast<SCENE_NS::IPostProcess>(
162         static_cast<CameraJS*>(rootobject)->CreateObject(SCENE_NS::ClassId::PostProcess));
163 
164     // create a postprocess object owned by CameraJS.
165 
166     // process constructor args..
167     NapiApi::Object meJs(fromJs.This());
168     // weak ref, as we expect to be owned by the camera.
169     SetNativeObject(interface_pointer_cast<META_NS::IObject>(postproc), false);
170     StoreJsObj(interface_pointer_cast<META_NS::IObject>(postproc), meJs);
171     // now, based on parameters, create correct objects.
172     if (NapiApi::Object args = fromJs.Arg<1>()) {
173         if (auto prm = args.Get("toneMapping")) {
174             // enable tonemap.
175             napi_value innerArgs[] = {
176                 meJs.ToNapiValue(), // postprocess
177                 prm                 // tonemap settings
178             };
179             SCENE_NS::ITonemap::Ptr tone = postproc->Tonemap()->GetValue();
180             MakeNativeObjectParam(e, tone, BASE_NS::countof(innerArgs), innerArgs);
181             NapiApi::Object tonemapJS(
182                 GetJSConstructor(e, "ToneMappingSettings"), BASE_NS::countof(innerArgs), innerArgs);
183             meJs.Set("toneMapping", tonemapJS);
184         }
185         if (auto prm = args.Get("bloom")) {
186             meJs.Set("bloom", prm);
187         }
188     }
189 }
190 
~PostProcJS()191 PostProcJS::~PostProcJS()
192 {
193     LOG_V("PostProcJS --");
194     DisposeNative(nullptr);
195     if (!GetNativeObject()) {
196         return;
197     }
198 }
199 
SetToneMapping(NapiApi::FunctionContext<NapiApi::Object> & ctx)200 void PostProcJS::SetToneMapping(NapiApi::FunctionContext<NapiApi::Object>& ctx)
201 {
202     auto postproc = interface_cast<SCENE_NS::IPostProcess>(GetNativeObject());
203     if (!postproc) {
204         // not possible.
205         return;
206     }
207     NapiApi::Object tonemapJS = ctx.Arg<0>();
208 
209     if (auto currentlySet = toneMap_.GetObject()) {
210         if (tonemapJS.StrictEqual(currentlySet)) { // setting the exactly the same tonemap setting. do nothing.
211             return;
212         }
213         // dispose the old bound object.. (actually should just convert to unbound state. and release the native object,
214         // which may or may not get disposed.)
215         NapiApi::Function func = currentlySet.Get<NapiApi::Function>("destroy");
216         if (func) {
217             func.Invoke(currentlySet);
218         }
219         toneMap_.Reset();
220     }
221 
222     TrueRootObject* native { nullptr };
223     SCENE_NS::ITonemap::Ptr tonemap;
224     // does the input parameter already have a bridge..
225     native = tonemapJS.Native<TrueRootObject>();
226     if (!native) {
227         // nope.. so create a new bridge object based on the input.
228         napi_value args[] = {
229             ctx.This().ToNapiValue(),  // postproc..
230             ctx.Arg<0>().ToNapiValue() // "javascript object for values"
231         };
232         NapiApi::Object res(GetJSConstructor(ctx.Env(), "ToneMappingSettings"), BASE_NS::countof(args), args);
233         native = res.Native<TrueRootObject>();
234         tonemapJS = res;
235 
236         // creating a new js object does have the side effect of not syncing modifications to input object to affect.
237         // you always need to GET a bound one from post proc to control it..
238         //
239         // we COULD forcefully bind to the input object in this case.
240         // but not sure what possible side effects would that have later on.
241     } else {
242         tonemap = interface_pointer_cast<SCENE_NS::ITonemap>(native->GetNativeObject());
243         postproc->Tonemap()->SetValue(tonemap);
244     }
245     toneMap_ = NapiApi::StrongRef(tonemapJS); // take ownership of the object.
246 }
247 
GetToneMapping(NapiApi::FunctionContext<> & ctx)248 napi_value PostProcJS::GetToneMapping(NapiApi::FunctionContext<>& ctx)
249 {
250     if (auto postproc = interface_cast<SCENE_NS::IPostProcess>(GetNativeObject())) {
251         SCENE_NS::ITonemap::Ptr tone = META_NS::GetValue(postproc->Tonemap());
252         if ((!tone) || (!META_NS::GetValue(tone->Enabled(), false))) {
253             // no tonemap object or tonemap disabled.
254             return ctx.GetUndefined();
255         }
256         auto obj = interface_pointer_cast<META_NS::IObject>(tone);
257 
258         if (auto cached = FetchJsObj(obj)) {
259             // always return the same js object.
260             return cached.ToNapiValue();
261         }
262 
263         napi_value args[] = {
264             ctx.This().ToNapiValue() // postproc..
265         };
266         MakeNativeObjectParam(ctx.GetEnv(), tone, BASE_NS::countof(args), args);
267         auto tonemapJS = CreateFromNativeInstance(ctx.GetEnv(), obj, false, BASE_NS::countof(args), args);
268         toneMap_ = NapiApi::StrongRef(tonemapJS); // take ownership of the object.
269         return tonemapJS.ToNapiValue();
270     }
271     toneMap_.Reset();
272     return ctx.GetUndefined();
273 }
GetBloom(NapiApi::FunctionContext<> & ctx)274 napi_value PostProcJS::GetBloom(NapiApi::FunctionContext<>& ctx)
275 {
276     if (!bloom_.IsEmpty()) {
277         // okay return the existing one.
278         return bloom_.GetValue();
279     }
280 
281     BloomConfiguration* data = nullptr;
282     if (auto postproc = interface_pointer_cast<SCENE_NS::IPostProcess>(GetNativeObject())) {
283         auto bloom = postproc->Bloom()->GetValue();
284         if (bloom->Enabled()->GetValue()) {
285             data = new BloomConfiguration();
286             data->SetFrom(bloom);
287             data->SetPostProc(postproc);
288         }
289     }
290 
291     if (data == nullptr) {
292         return ctx.GetUndefined();
293     }
294 
295     NapiApi::Env env(ctx.GetEnv());
296     NapiApi::Object obj(env);
297     bloom_ = BASE_NS::move(data->Wrap(obj));
298     return bloom_.GetValue();
299 }
300 
SetBloom(NapiApi::FunctionContext<NapiApi::Object> & ctx)301 void PostProcJS::SetBloom(NapiApi::FunctionContext<NapiApi::Object>& ctx)
302 {
303     auto postproc = interface_pointer_cast<SCENE_NS::IPostProcess>(GetNativeObject());
304     if (!postproc) {
305         return;
306     }
307     BloomConfiguration* settings = nullptr;
308     NapiApi::Env env(ctx.GetEnv());
309     NapiApi::Object inputObj = ctx.Arg<0>();
310     bool enabled = false;
311     if (inputObj) {
312         enabled = true;
313         settings = BloomConfiguration::Unwrap(inputObj);
314         // is it wrapped?
315         if (settings) {
316             auto boundpost = settings->GetPostProc();
317             if ((boundpost) && (boundpost != postproc)) {
318                 // silently fail, we can not use settings object from another postprocess object yet.
319                 LOG_F("Tried to attach a bloom setting object that was already bound to another one");
320             } else {
321                 // has wrapper, but no native postproc. (or the bound one is the same)
322                 // so we can use it.
323                 settings->SetPostProc(postproc);
324                 // save the reference..
325                 bloom_ = BASE_NS::move(NapiApi::StrongRef(inputObj));
326             }
327         } else {
328             settings = new BloomConfiguration();
329             settings->SetFrom(inputObj);
330             settings->SetPostProc(postproc);
331             bloom_ = settings->Wrap(inputObj);
332         }
333     } else {
334         // disabling bloom.. get current, and unwrap it.
335         auto oldObj = bloom_.GetObject();
336         settings = BloomConfiguration::Unwrap(oldObj);
337         if (settings) {
338             // detaches native and javascript object.
339             settings->SetPostProc(nullptr); // detach from object
340         }
341         // release our reference to the current bloom settings (JS)
342         bloom_.Reset();
343         enabled = false;
344     }
345     if (SCENE_NS::IBloom::Ptr bloom = postproc->Bloom()->GetValue()) {
346         bloom->Enabled()->SetValue(enabled);
347         postproc->Bloom()->SetValue(bloom);
348     }
349 }
350