@@ -63,14 +63,28 @@ class Loader::Impl
63
63
void pre_load_model (LoaderOptions& this_options, bool & this_ok_missing,
64
64
boost::optional<bool > no_cache, bool allow_instance, boost::optional<bool > ok_missing);
65
65
66
+ /* *
67
+ * A model or sound file or some such thing has just been
68
+ * loaded asynchronously by the sub-thread. Add it to the list
69
+ * of loaded objects, and call the appropriate callback when it's
70
+ * time.
71
+ */
72
+ void got_async_object (const Event* ev);
73
+
66
74
public:
75
+ static size_t loader_index_;
76
+
67
77
ShowBase& base_;
68
78
::Loader* loader_;
79
+
80
+ std::string hook_;
81
+ std::unordered_map<AsyncTask*, std::pair<std::shared_ptr<Callback>, size_t >> requests_;
69
82
};
70
83
84
+ size_t Loader::Impl::loader_index_ = 0 ;
85
+
71
86
Loader::Impl::Impl (ShowBase& base): base_(base)
72
87
{
73
- loader_ = ::Loader::get_global_ptr ();
74
88
}
75
89
76
90
void Loader::Impl::pre_load_model (LoaderOptions& this_options, bool & this_ok_missing,
@@ -101,19 +115,114 @@ void Loader::Impl::pre_load_model(LoaderOptions& this_options, bool& this_ok_mis
101
115
this_options.set_flags (this_options.get_flags () | LoaderOptions::LF_allow_instance);
102
116
}
103
117
118
+ void Loader::Impl::got_async_object (const Event* ev)
119
+ {
120
+ if (ev->get_num_parameters () != 1 )
121
+ {
122
+ rppanda_showbase_cat.error () << " Invalid number of paramter: " << ev->get_num_parameters () << std::endl;
123
+ return ;
124
+ }
125
+
126
+ const auto & param = ev->get_parameter (0 );
127
+ auto request = DCAST (AsyncTask, param.get_typed_ref_count_value ());
128
+
129
+ auto found = requests_.find (request);
130
+ if (found == requests_.end ())
131
+ return ;
132
+
133
+ auto cb = found->second .first ; // should hold callback for Callback::get_object
134
+ auto i = found->second .second ;
135
+
136
+ if (cb->cancelled () || request->cancelled ())
137
+ {
138
+ // Shouldn't be here.
139
+ requests_.erase (request);
140
+ return ;
141
+ }
142
+
143
+ cb->requests_ .erase (request);
144
+ if (!cb->requests_ .empty ())
145
+ requests_.erase (request);
146
+
147
+ PandaNode* result = DCAST (PandaNode, request->get_result ());
148
+
149
+ cb->got_object (i, NodePath (result));
150
+ }
151
+
152
+ // ************************************************************************************************
153
+
154
+ Loader::Callback::Callback (Loader* loader, int num_objects,
155
+ const std::function<void (const std::vector<NodePath>&)>& callback) : loader_(loader), callback_(callback)
156
+ {
157
+ objects_.resize (num_objects);
158
+ }
159
+
160
+ void Loader::Callback::got_object (size_t index, NodePath object)
161
+ {
162
+ objects_[index ] = object;
163
+
164
+ if (!done ())
165
+ return ;
166
+
167
+ loader_ = nullptr ;
168
+ if (callback_)
169
+ callback_ (objects_);
170
+ }
171
+
172
+ void Loader::Callback::cancel ()
173
+ {
174
+ if (!loader_)
175
+ return ;
176
+
177
+ for (const auto & request : requests_)
178
+ {
179
+ loader_->impl_ ->loader_ ->remove (request);
180
+ loader_->impl_ ->requests_ .erase (request);
181
+ }
182
+
183
+ loader_ = nullptr ;
184
+ requests_.clear ();
185
+ request_list_.clear ();
186
+ }
187
+
188
+ bool Loader::Callback::cancelled () const
189
+ {
190
+ return request_list_.empty ();
191
+ }
192
+
193
+ bool Loader::Callback::done () const
194
+ {
195
+ return requests_.empty ();
196
+ }
197
+
198
+ void Loader::Callback::wait () const
199
+ {
200
+ for (const auto & r : requests_)
201
+ r->wait ();
202
+ }
203
+
104
204
// ************************************************************************************************
105
205
106
206
TypeHandle Loader::type_handle_;
107
207
108
208
Loader::Loader (ShowBase& base): impl_(std::make_unique<Impl>(base))
109
209
{
210
+ impl_->loader_ = ::Loader::get_global_ptr ();
211
+
212
+ impl_->hook_ = " async_loader_" + std::to_string (Loader::Impl::loader_index_);
213
+ Loader::Impl::loader_index_ += 1 ;
214
+ accept (impl_->hook_ , std::bind (&Impl::got_async_object, impl_.get (), std::placeholders::_1));
110
215
}
111
216
112
217
#if !defined(_MSC_VER) || _MSC_VER >= 1900
113
218
Loader::Loader (Loader&&) = default;
114
219
#endif
115
220
116
- Loader::~Loader () = default ;
221
+ Loader::~Loader ()
222
+ {
223
+ ignore (impl_->hook_ );
224
+ impl_->loader_ ->stop_threads ();
225
+ }
117
226
118
227
#if !defined(_MSC_VER) || _MSC_VER >= 1900
119
228
Loader& Loader::operator =(Loader&&) = default ;
@@ -122,7 +231,8 @@ Loader& Loader::operator=(Loader&&) = default;
122
231
NodePath Loader::load_model (const Filename& model_path, const LoaderOptions& loader_options,
123
232
boost::optional<bool > no_cache, bool allow_instance, boost::optional<bool > ok_missing)
124
233
{
125
- return load_model (std::vector<Filename>{model_path}, loader_options, no_cache, allow_instance, ok_missing).front ();
234
+ return load_model (std::vector<Filename>{model_path}, loader_options, no_cache,
235
+ allow_instance, ok_missing).front ();
126
236
}
127
237
128
238
std::vector<NodePath> Loader::load_model (const std::vector<Filename>& model_list, const LoaderOptions& loader_options,
@@ -134,6 +244,7 @@ std::vector<NodePath> Loader::load_model(const std::vector<Filename>& model_list
134
244
bool this_ok_missing;
135
245
impl_->pre_load_model (this_options, this_ok_missing, no_cache, allow_instance, ok_missing);
136
246
247
+ // We got no callback, so it's a synchronous load.
137
248
std::vector<NodePath> result;
138
249
for (const auto & model_path: model_list)
139
250
{
@@ -151,6 +262,50 @@ std::vector<NodePath> Loader::load_model(const std::vector<Filename>& model_list
151
262
return result;
152
263
}
153
264
265
+ std::shared_ptr<Loader::Callback> Loader::load_model_async (const Filename& model_path, const LoaderOptions& loader_options,
266
+ boost::optional<bool > no_cache, bool allow_instance, boost::optional<bool > ok_missing,
267
+ const std::function<void (const std::vector<NodePath>&)>& callback,
268
+ boost::optional<int> priority)
269
+ {
270
+ return load_model_async (std::vector<Filename>{model_path}, loader_options, no_cache,
271
+ allow_instance, ok_missing);
272
+ }
273
+
274
+ std::shared_ptr<Loader::Callback> Loader::load_model_async (const std::vector<Filename>& model_list, const LoaderOptions& loader_options,
275
+ boost::optional<bool > no_cache, bool allow_instance, boost::optional<bool > ok_missing,
276
+ const std::function<void (const std::vector<NodePath>&)>& callback,
277
+ boost::optional<int> priority)
278
+ {
279
+ rppanda_showbase_cat.debug () << " Loading model: " << join_to_string (model_list) << std::endl;
280
+
281
+ LoaderOptions this_options (loader_options);
282
+ bool this_ok_missing;
283
+ impl_->pre_load_model (this_options, this_ok_missing, no_cache, allow_instance, ok_missing);
284
+
285
+ // We got a callback, so we want an asynchronous(threaded)
286
+ // load.We'll return immediately, but when all of the
287
+ // requested models have been loaded, we'll invoke the
288
+ // callback(passing it the models on the parameter list).
289
+
290
+ auto cb = std::make_shared<Callback>(this , model_list.size (), callback);
291
+
292
+ size_t i = 0 ;
293
+ for (const auto & model_path: model_list)
294
+ {
295
+ PT (AsyncTask) request = impl_->loader_ ->make_async_request (model_path, this_options);
296
+ if (priority)
297
+ request->set_priority (priority.value ());
298
+ request->set_done_event (impl_->hook_ );
299
+ impl_->loader_ ->load_async (request);
300
+ cb->requests_ .insert (request);
301
+ cb->request_list_ .push_back (request);
302
+ impl_->requests_ .insert ({request.p (), {cb, i}});
303
+ i += 1 ;
304
+ }
305
+
306
+ return cb;
307
+ }
308
+
154
309
PT (TextFont) Loader::load_font(const std::string& model_path,
155
310
boost::optional<float > space_advance, boost::optional<float > line_height,
156
311
boost::optional<float > point_size,
0 commit comments