Unravel Engine C++ Reference
Loading...
Searching...
No Matches
asset_watcher.cpp
Go to the documentation of this file.
1#include "asset_watcher.h"
2#include "engine/ui/ui_tree.h"
4#include "threadpp/thread.h"
14#include <engine/ecs/ecs.h>
15#include <engine/ecs/prefab.h>
16#include <engine/events.h>
24
25
28
32
33#include <filesystem/watcher.h>
34#include <graphics/graphics.h>
35#include <logging/logging.h>
36
37#include <algorithm>
38#include <set>
39
40namespace unravel
41{
42namespace
43{
44using namespace std::literals;
45
46template<typename T>
47auto get_job_name() -> std::string
48{
49 return fmt::format("Compiling {}", ex::get_type<T>());
50}
51
52template<typename T>
53auto checking_dependencies_job_name() -> std::string
54{
55 return fmt::format("Checking dependencies of {}", ex::get_type<T>());
56}
57
58
59template<typename T>
60auto checking_for_recompilation_job_name() -> std::string
61{
62 return fmt::format("Checking for recompilation of {}", ex::get_type<T>());
63}
64
65
66
67auto get_absolute_source_path(const fs::path& source_file_path) -> fs::path
68{
69 auto absolute_source = fs::resolve_protocol(
71 if(absolute_source.extension() == ".meta")
72 {
73 absolute_source.replace_extension();
74 }
75 return absolute_source;
76}
77
81template<typename T>
82auto needs_recompilation(const fs::path& source_file_path, const fs::path& compiled_output_path) -> bool
83{
84 fs::error_code err;
85 if(!fs::exists(compiled_output_path, err) || err)
86 {
87 APPLOG_WARNING("Compiled output does not exist for {}, recompilation needed", compiled_output_path.string());
88 return true;
89 }
90 auto manifest_path = asset_compiler::get_manifest_path(compiled_output_path);
91 if(!fs::exists(manifest_path, err) || err)
92 {
93 APPLOG_WARNING("Manifest does not exist for {}, recompilation needed", compiled_output_path.string());
94 return true;
95 }
96 asset_compiler::asset_manifest manifest;
97 if(!asset_compiler::load_manifest(manifest_path, manifest))
98 {
99 APPLOG_WARNING("Failed to load manifest for {}, recompilation needed", compiled_output_path.string());
100 return true;
101 }
102
103 if(asset_compiler::is_compiled_format_changed(source_file_path, manifest))
104 {
105 APPLOG_WARNING("Compiled format changed for {}, recompilation needed", compiled_output_path.string());
106 return true;
107 }
108
109 std::vector<fs::path> deps;
110 asset_compiler::resolve_dependencies<T>(get_absolute_source_path(source_file_path), deps);
111 if(asset_compiler::is_source_file_changed(source_file_path, manifest, deps))
112 {
113 APPLOG_WARNING("Source file changed for {}, recompilation needed", compiled_output_path.string());
114 return true;
115 }
116
117 return false;
118}
119
120template<typename T>
121auto has_depencency(const fs::path& file, const fs::path& dep_to_check) -> bool
122{
123 std::vector<fs::path> dependecies;
125 return std::find(dependecies.begin(), dependecies.end(), dep_to_check) != dependecies.end();
126}
127
128auto remove_meta_tag(const fs::path& synced_path) -> fs::path
129{
130 return fs::replace(synced_path, ".meta", "");
131}
132
133auto remove_meta_tag(const std::vector<fs::path>& synced_paths) -> std::vector<fs::path>
134{
135 std::decay_t<decltype(synced_paths)> reduced;
136 reduced.reserve(synced_paths.size());
137 for(const auto& synced_path : synced_paths)
138 {
139 reduced.emplace_back(remove_meta_tag(synced_path));
140 }
141 return reduced;
142}
143
144void unwatch(std::vector<uint64_t>& watchers)
145{
146 for(const auto& id : watchers)
147 {
149 }
150 watchers.clear();
151}
152
153auto get_asset_key(const fs::path& path) -> std::string
154{
156 auto data_key = fs::convert_to_protocol(p);
157 auto key =
158 fs::replace(data_key.generic_string(), ex::get_compiled_directory(), ex::get_data_directory()).generic_string();
159 return key;
160}
161
162auto get_meta_key(const fs::path& path) -> std::string
163{
165 auto data_key = fs::convert_to_protocol(p);
166 auto key =
167 fs::replace(data_key.generic_string(), ex::get_compiled_directory(), ex::get_meta_directory()).generic_string();
168 return key + ".meta";
169}
170
171auto check_files_integrity(const std::string& key, const fs::path& entry_path) -> bool
172{
173 fs::error_code ec;
174 auto key_path = fs::resolve_protocol(key);
175
176 if(!fs::exists(key_path, ec))
177 {
178 APPLOG_WARNING("{} does not exist. Cleaning up compiled...", key);
179 fs::remove(entry_path, ec);
180 auto manifest_path = asset_compiler::get_manifest_path(entry_path);
181 fs::remove(manifest_path, ec);
182
183
184 auto meta = get_meta_key(entry_path);
185 auto meta_path = fs::resolve_protocol(meta);
186 if(fs::exists(meta_path, ec))
187 {
188 APPLOG_WARNING("{} does not exist. Cleaning up meta...", key);
189 fs::remove(meta_path, ec);
190 }
191
192 return false;
193 }
194
195 return true;
196}
197
198template<typename T>
199auto watch_assets(rtti::context& ctx, const fs::path& dir, const fs::pattern_filter& filter, bool reload_async)
200 -> uint64_t
201{
202 auto& am = ctx.get_cached<asset_manager>();
203 auto& ts = ctx.get_cached<threader>();
204 auto& tm = ctx.get_cached<thumbnail_manager>();
205 auto& em = ctx.get_cached<editing_manager>();
206
207 fs::path watch_dir = fs::path(dir).make_preferred();
208 hpp::source_location loc = hpp::source_location::current();
209 auto callback = [&am, &ts, &tm, &em, loc](const auto& entries, bool is_initial_list)
210 {
211 std::set<hpp::uuid> changed;
212 std::set<hpp::uuid> removed;
213 // Dependents of every UUID in `removed`, computed *before* unload.
214 // Once `am.unload_asset` runs it invalidates the shared
215 // `asset_link_t`, after which other handles (e.g. a material's
216 // texture slot) report nil UUIDs and dependent enumeration silently
217 // misses them. Capturing here preserves the still-valid chain.
218 std::set<hpp::uuid> removed_dependents;
219
220 for(const auto& entry : entries)
221 {
222 auto key = get_asset_key(entry.path);
223
224 // APPLOG_TRACE_LOC(loc.file_name(), int(loc.line()), loc.function_name(), "{}", fs::to_string(entry));
225
226 if(entry.type == fs::file_type::regular)
227 {
229 {
230 auto asset = am.find_asset<T>(key);
231 if(asset)
232 {
233 const auto uid = asset.uid();
234 removed.emplace(uid);
235
236 const auto deps = asset_deps::find_transitive_loaded_dependents(am, uid);
237 removed_dependents.insert(deps.begin(), deps.end());
238
239 am.unload_asset<T>(key);
240 }
241
242 if constexpr(std::is_same<T, script>::value)
243 {
245 }
246 }
248 {
249 auto old_key = get_asset_key(entry.last_path);
250 am.rename_asset<T>(old_key, key);
251
252 if constexpr(std::is_same<T, script>::value)
253 {
255 }
256 }
257 else // created or modified
258 {
259 if(check_files_integrity(key, entry.path))
260 {
261 load_flags flags = is_initial_list ? load_flags::standard : load_flags::reload;
262 auto asset = am.get_asset<T>(key, flags, load_mode::deferred);
263 if(asset)
264 {
265 changed.emplace(asset.uid());
266 }
267 }
268
269 if constexpr(std::is_same<T, script>::value)
270 {
272 }
273 }
274 }
275 }
276
277 if(!changed.empty() || !removed.empty())
278 {
279 tpp::invoke(tpp::main_thread::get_id(),
280 [&tm, &em, &am, changed, removed, removed_dependents]()
281 {
282 // A change in any of these types can visually
283 // affect a prefab thumbnail (texture in a
284 // material, material on a mesh, mesh in a
285 // model_component, animation on a prefab).
286 // Because prefabs are stored as opaque serialized
287 // buffers we don't walk them for explicit
288 // references — instead the cascade conservatively
289 // marks every loaded prefab thumbnail dirty when
290 // one of these renderable types changes.
291 constexpr bool affects_prefabs =
292 std::is_same_v<T, gfx::texture> ||
293 std::is_same_v<T, material> ||
294 std::is_same_v<T, mesh> ||
295 std::is_same_v<T, animation_clip>;
296
297 // Deletions: drop the removed thumbnails, regen
298 // every (pre-captured) dependent so missing-slot
299 // visuals propagate.
301 tm,
302 removed,
303 removed_dependents,
304 affects_prefabs);
305
306 for(const auto& uid : changed)
307 {
308 asset_deps::cascade_thumbnail_regen(am, tm, uid, affects_prefabs);
309
310 if constexpr(std::is_same<T, prefab>::value)
311 {
312 auto asset = am.get_asset<T>(uid);
313 em.on_prefab_updated(asset);
314 }
315 }
316 });
317 }
318 };
319
320 std::stringstream ss;
321 for(const auto& pattern : filter.get_include_patterns())
322 {
323 ss << pattern.get_pattern() << " ";
324 }
325 return fs::watcher::watch(watch_dir, filter, true, true, 500ms, callback, "Asset Watcher " + ss.str());
326}
327
328template<typename T>
329auto watch_assets_depenencies(rtti::context& ctx, const fs::path& dir, const fs::pattern_filter& filter) -> uint64_t
330{
331 auto& am = ctx.get_cached<asset_manager>();
332 auto& ts = ctx.get_cached<threader>();
333
334 fs::path watch_dir = fs::path(dir).make_preferred();
335
336 auto callback = [&am, &ts](const auto& entries, bool is_initial_list)
337 {
338 if(is_initial_list)
339 {
340 return;
341 }
342
343 for(const auto& entry : entries)
344 {
345 // APPLOG_TRACE("{}", fs::to_string(entry));
346
347 if(entry.type == fs::file_type::regular)
348 {
350 {
351 }
353 {
354 }
355 else // created or modified
356 {
357 auto task = ts.pool->schedule(checking_dependencies_job_name<T>(),
358 [&am, entry]()
359 {
360 auto assets = am.get_assets<T>();
361 for(const auto& asset : assets)
362 {
363 auto meta = am.get_metadata(asset.uid());
364 auto absolute_path = fs::resolve_protocol(meta.location);
365
366 if(has_depencency<T>(absolute_path, entry.path))
367 {
368 fs::watcher::touch(absolute_path, false);
369 }
370 }
371 });
372 }
373 }
374 }
375 };
376
377 std::stringstream ss;
378 for(const auto& pattern : filter.get_include_patterns())
379 {
380 ss << pattern.get_pattern() << " ";
381 }
382 return fs::watcher::watch(watch_dir, filter, true, true, 500ms, callback, "Asset Dependencies Watcher " + ss.str());
383}
384
385template<typename T>
386static void add_to_syncer(rtti::context& ctx,
387 fs::syncer& syncer,
388 const fs::syncer::on_entry_removed_t& on_removed,
389 const fs::syncer::on_entry_renamed_t& on_renamed)
390{
391 auto& ts = ctx.get_cached<threader>();
392 auto& am = ctx.get_cached<asset_manager>();
393
394 auto on_modified =
395 [&ts, &am](const std::string& ext, const auto& ref_path, const auto& synced_paths, bool is_initial_listing)
396 {
397 auto paths = remove_meta_tag(synced_paths);
398
399 for(const auto& output : paths)
400 {
401 if(is_initial_listing && !needs_recompilation<T>(ref_path, output))
402 {
403 continue;
404 }
405
406 auto key = get_asset_key(output);
407 if(check_files_integrity(key, output))
408 {
409 auto job_name = fmt::format("Compiling {}", ex::get_type<T>());
410 auto task = ts.pool->schedule(job_name,
411 [&am, ref_path, output, job_name]()
412 {
413 APPLOG_TRACE_PERF_NAMED_ALLOC(std::chrono::milliseconds, fmt::format("{} - {}", job_name, output.string()));
414 asset_compiler::compile<T>(am, ref_path, output);
415 });
416 }
417 }
418 };
419
420 for(const auto& type : ex::get_suported_formats<T>())
421 {
422 syncer.set_mapping(type + ".meta",
423 {".asset"},
424 on_modified,
425 on_modified,
426 on_removed,
427 on_renamed);
428 }
429}
430
431template<typename T>
432static void watch_synced(rtti::context& ctx, std::vector<uint64_t>& watchers, const fs::path& dir)
433{
434 for(const auto& type : ex::get_suported_formats<T>())
435 {
436 const auto watch_id = watch_assets<T>(ctx, dir, fs::pattern_filter("*" + type + ".asset"), true);
437 watchers.push_back(watch_id);
438 }
439}
440
441template<>
442void add_to_syncer<gfx::shader>(rtti::context& ctx,
443 fs::syncer& syncer,
444 const fs::syncer::on_entry_removed_t& on_removed,
445 const fs::syncer::on_entry_renamed_t& on_renamed)
446{
447 auto& ts = ctx.get_cached<threader>();
448 auto& am = ctx.get_cached<asset_manager>();
449
450 auto on_modified =
451 [&ts, &am](const std::string& ext, const auto& ref_path, const auto& synced_paths, bool is_initial_listing)
452 {
453 auto paths = remove_meta_tag(synced_paths);
454 if(paths.empty())
455 {
456 return;
457 }
458 const auto& platform_supported = gfx::get_renderer_platform_supported_filename_extensions();
459
460 for(const auto& output : paths)
461 {
462 auto extension = output.extension().string();
463 auto it =
464 std::find(std::begin(platform_supported), std::end(platform_supported), extension);
465
466 if(it == std::end(platform_supported))
467 {
468 continue;
469 }
470
471 if(is_initial_listing && !needs_recompilation<gfx::shader>(ref_path, output))
472 {
473 continue;
474 }
475
476 auto key = get_asset_key(output);
477 if(check_files_integrity(key, output))
478 {
479 bool high_priority = gfx::get_current_renderer_filename_extension() == extension;
480 tpp::priority::group priority = high_priority ? tpp::priority::normal() : tpp::priority::low();
481
482 auto job_name = fmt::format("Compiling {}", ex::get_type<gfx::shader>() + "(" + extension + ")");
483 auto task = ts.pool->schedule(job_name,
484 priority,
485 [&am, ref_path, output, job_name]()
486 {
487 // APPLOG_TRACE_PERF_NAMED_ALLOC(std::chrono::milliseconds, fmt::format("{} - {}", job_name, output.string()));
488 asset_compiler::compile<gfx::shader>(am, ref_path, output);
489 });
490 }
491 }
492 };
493
494 const auto& platform_supported = gfx::get_renderer_platform_supported_filename_extensions();
495 std::vector<std::string> supported_extensions;
496 supported_extensions.reserve(platform_supported.size());
497 for(const auto& extension : platform_supported)
498 {
499 supported_extensions.push_back(".asset" + extension);
500 }
502 {
503 syncer.set_mapping(type + ".meta",
504 supported_extensions,
505 on_modified,
506 on_modified,
507 on_removed,
508 on_renamed);
509 }
510}
511
512template<>
513void watch_synced<gfx::shader>(rtti::context& ctx, std::vector<uint64_t>& watchers, const fs::path& dir)
514{
515 const auto& renderer_extension = gfx::get_current_renderer_filename_extension();
517 {
518 const auto watch_id =
519 watch_assets<gfx::shader>(ctx, dir, fs::pattern_filter("*" + type + ".asset" + renderer_extension), true);
520 watchers.push_back(watch_id);
521 }
522}
523
524} // namespace
525
526void asset_watcher::setup_directory(rtti::context& ctx, fs::syncer& syncer)
527{
528 const auto on_dir_modified =
529 [](const std::string& ext, const auto& /*ref_path*/, const auto& /*synced_paths*/, bool /*is_initial_listing*/)
530 {
531
532 };
533 const auto on_dir_removed = [](const std::string& ext, const auto& /*ref_path*/, const auto& synced_paths)
534 {
535 for(const auto& synced_path : synced_paths)
536 {
537 fs::error_code err;
538 fs::remove_all(synced_path, err);
539 }
540 };
541
542 const auto on_dir_renamed = [](const std::string& ext, const auto& /*ref_path*/, const auto& synced_paths)
543 {
544 for(const auto& synced_path : synced_paths)
545 {
546 fs::error_code err;
547 fs::rename(synced_path.first, synced_path.second, err);
548 }
549 };
550 syncer.set_directory_mapping(on_dir_modified, on_dir_modified, on_dir_removed, on_dir_renamed);
551}
552
553void asset_watcher::setup_meta_syncer(rtti::context& ctx,
554 std::vector<uint64_t>& watchers,
555 fs::syncer& syncer,
556 const fs::path& data_dir,
557 const fs::path& meta_dir,
558 bool wait,
559 const on_wait_progress_t& on_progress)
560{
561 setup_directory(ctx, syncer);
562 auto& am = ctx.get_cached<asset_manager>();
563
564 const auto on_file_removed = [&am](const std::string& ext, const auto& ref_path, const auto& synced_paths)
565 {
566 for(const auto& synced_path : synced_paths)
567 {
568 fs::error_code err;
569 fs::remove_all(synced_path, err);
570
571 am.remove_asset_info_for_path(ref_path);
572 }
573 };
574
575 const auto on_file_renamed = [](const std::string& ext, const auto& /*ref_path*/, const auto& synced_paths)
576 {
577 for(const auto& synced_path : synced_paths)
578 {
579 fs::error_code err;
580 fs::rename(synced_path.first, synced_path.second, err);
581 }
582 };
583
584 const auto on_file_modified =
585 [&am, this](const std::string& ext, const auto& ref_path, const auto& synced_paths, bool is_initial_listing)
586 {
587 for(const auto& synced_path : synced_paths)
588 {
589 asset_meta meta;
590 fs::error_code err;
591 if(fs::exists(synced_path, err))
592 {
593 load_from_file(synced_path.string(), meta);
594 }
595
596 bool recreate_meta_file = false;
597 {
598 std::lock_guard<std::mutex> lock(recreate_meta_files_queue_mutex_);
599 if(!recreate_meta_files_queue_.empty())
600 {
601 auto it = recreate_meta_files_queue_.find(ref_path.string());
602 if(it != recreate_meta_files_queue_.end())
603 {
604 recreate_meta_files_queue_.erase(it);
605
606 recreate_meta_file = true;
607 }
608 }
609 }
610
611
612 if(meta.uid.is_nil() || recreate_meta_file)
613 {
614 auto new_meta = am.get_metadata_for_path(ref_path).meta;
615
616 if(new_meta.uid.is_nil())
617 {
618 const auto protocol_path = fs::convert_to_protocol(ref_path);
619 new_meta = am.generate_metadata(protocol_path);
620 am.remove_asset_info_for_path(ref_path);
621 }
622
623 if(new_meta.uid != meta.uid)
624 {
625 meta = new_meta;
626 }
627 }
628 meta.uid = am.add_asset_info_for_path(ref_path, meta, true);
629
630 // asset_writer::atomic_write_file(synced_path.string(), [&](const fs::path& temp) -> void
631 // {
632 // save_to_file(temp.string(), meta);
633 // }, err);
634
635 save_to_file(synced_path.string(), meta);
636 }
637 };
638
639 for(const auto& asset_set : ex::get_all_formats())
640 {
641 for(const auto& type : asset_set)
642 {
643 syncer.set_mapping(type, {".meta"}, on_file_modified, on_file_modified, on_file_removed, on_file_renamed);
644 }
645 }
646
648 {
649 auto id = watch_assets_depenencies<gfx::shader>(ctx, data_dir, fs::pattern_filter("*" + dep_ex));
650 watchers.emplace_back(id);
651 }
652
653 for(const auto& dep_ex : ex::get_suported_dependencies_formats<ui_tree>())
654 {
655 auto id = watch_assets_depenencies<ui_tree>(ctx, data_dir, fs::pattern_filter("*" + dep_ex));
656 watchers.emplace_back(id);
657 }
658
659 auto on_sync_progress = [on_progress](size_t completed, size_t total, const std::string& current_job)
660 {
661 if(on_progress)
662 {
663 on_progress(completed, total, "Indexing Assets Metadata " + current_job);
664 }
665 };
666
667 syncer.sync(data_dir, meta_dir, on_sync_progress);
668
669 if(wait)
670 {
671 auto& ts = ctx.get_cached<threader>();
672 APPLOG_TRACE("Waiting for jobs to complete... (this may take a while)");
673
674
675 ts.pool->wait_all_polling(tpp::priority::category::normal,
676 [&on_progress](const tpp::thread_pool::progress_info& info)
677 {
678 if(on_progress)
679 {
680 on_progress(info.current_job, info.total_jobs, info.name);
681 }
682 });
683 }
684}
685
686void asset_watcher::setup_cache_syncer(rtti::context& ctx,
687 std::vector<uint64_t>& watchers,
688 fs::syncer& syncer,
689 const fs::path& meta_dir,
690 const fs::path& cache_dir,
691 bool wait,
692 const on_wait_progress_t& on_progress)
693{
694 setup_directory(ctx, syncer);
695
696 auto on_removed = [](const std::string& ext, const auto& /*ref_path*/, const auto& synced_paths)
697 {
698 for(const auto& synced_path : synced_paths)
699 {
700 auto synced_asset = remove_meta_tag(synced_path);
701
702 auto manifest_path = asset_compiler::get_manifest_path(synced_asset);
703 fs::error_code err;
704 fs::remove_all(manifest_path, err);
705 fs::remove_all(synced_asset, err);
706 }
707 };
708
709 auto on_renamed = [](const std::string& ext, const auto& /*ref_path*/, const auto& synced_paths)
710 {
711 for(const auto& synced_path : synced_paths)
712 {
713 auto synced_old_asset = remove_meta_tag(synced_path.first);
714 auto synced_new_asset = remove_meta_tag(synced_path.second);
715 auto manifest_old_path = asset_compiler::get_manifest_path(synced_old_asset);
716 auto manifest_new_path = asset_compiler::get_manifest_path(synced_new_asset);
717 fs::error_code err;
718 fs::rename(manifest_old_path, manifest_new_path, err);
719 fs::rename(synced_old_asset, synced_new_asset, err);
720 }
721 };
722
723 add_to_syncer<gfx::texture>(ctx, syncer, on_removed, on_renamed);
724 add_to_syncer<gfx::shader>(ctx, syncer, on_removed, on_renamed);
725 add_to_syncer<mesh>(ctx, syncer, on_removed, on_renamed);
726 add_to_syncer<material>(ctx, syncer, on_removed, on_renamed);
727 add_to_syncer<animation_clip>(ctx, syncer, on_removed, on_renamed);
728 add_to_syncer<prefab>(ctx, syncer, on_removed, on_renamed);
729 add_to_syncer<scene_prefab>(ctx, syncer, on_removed, on_renamed);
730 add_to_syncer<physics_material>(ctx, syncer, on_removed, on_renamed);
731 add_to_syncer<audio_clip>(ctx, syncer, on_removed, on_renamed);
732 add_to_syncer<font>(ctx, syncer, on_removed, on_renamed);
733 add_to_syncer<script>(ctx, syncer, on_removed, on_renamed);
734 add_to_syncer<ui_tree>(ctx, syncer, on_removed, on_renamed);
735 add_to_syncer<style_sheet>(ctx, syncer, on_removed, on_renamed);
736
737 auto on_sync_progress = [on_progress](size_t completed, size_t total, const std::string& current_job)
738 {
739 if(on_progress)
740 {
741 on_progress(completed, total, "Indexing Compiled Assets " + current_job);
742 }
743 };
744 syncer.sync(meta_dir, cache_dir, on_sync_progress);
745
746 if(wait)
747 {
748 auto& ts = ctx.get_cached<threader>();
749 APPLOG_TRACE("Waiting for jobs to complete... (this may take a while)");
750 hpp::source_location loc = hpp::source_location::current();
751 ts.pool->wait_all_polling(tpp::priority::category::normal,
752 [&on_progress](const tpp::thread_pool::progress_info& info)
753 {
754 if(on_progress)
755 {
756 on_progress(info.current_job, info.total_jobs, info.name);
757 }
758 });
759 }
760
761 watch_synced<gfx::texture>(ctx, watchers, cache_dir);
762 watch_synced<gfx::shader>(ctx, watchers, cache_dir);
763 watch_synced<mesh>(ctx, watchers, cache_dir);
764 watch_synced<material>(ctx, watchers, cache_dir);
765 watch_synced<animation_clip>(ctx, watchers, cache_dir);
766 watch_synced<prefab>(ctx, watchers, cache_dir);
767 watch_synced<scene_prefab>(ctx, watchers, cache_dir);
768 watch_synced<physics_material>(ctx, watchers, cache_dir);
769 watch_synced<audio_clip>(ctx, watchers, cache_dir);
770 watch_synced<font>(ctx, watchers, cache_dir);
771 watch_synced<script>(ctx, watchers, cache_dir);
772 watch_synced<ui_tree>(ctx, watchers, cache_dir);
773 watch_synced<style_sheet>(ctx, watchers, cache_dir);
774}
775
779
783
784void asset_watcher::on_os_event(rtti::context& ctx, os::event& e)
785{
786 if(e.type == os::events::window)
787 {
788 if(e.window.type == os::window_event_id::focus_lost)
789 {
790 if(!os::window::is_any_focused())
791 {
792 // APPLOG_TRACE("Application lost focus");
793 // fs::watcher::pause();
794 }
795 }
796 if(e.window.type == os::window_event_id::focus_gained)
797 {
798 if(os::window::is_any_focused())
799 {
800 // APPLOG_TRACE("Application gained focus");
801 // fs::watcher::resume();
802 }
803 }
804 }
805}
806
808{
809 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
810
811 auto& ev = ctx.get_cached<events>();
812 ev.on_os_event.connect(sentinel_, 1000, this, &asset_watcher::on_os_event);
813
814 return true;
815}
816
818{
819 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
820
821 return true;
822}
823
825 const std::string& protocol,
826 bool wait,
827 const on_wait_progress_t& on_progress)
828{
829 auto start_time = std::chrono::steady_clock::now();
830 auto& w = watched_protocols_[protocol];
831
832 auto data_protocol = ex::get_data_directory_no_slash(protocol);
833 auto meta_protocol = ex::get_meta_directory_no_slash(protocol);
834 auto cache_protocol = ex::get_compiled_directory_no_slash(protocol);
835
836 setup_meta_syncer(ctx,
837 w.watchers,
838 w.meta_syncer,
839 fs::resolve_protocol(data_protocol),
840 fs::resolve_protocol(meta_protocol),
841 wait,
842 on_progress);
843
844 setup_cache_syncer(ctx,
845 w.watchers,
846 w.cache_syncer,
847 fs::resolve_protocol(meta_protocol),
848 fs::resolve_protocol(cache_protocol),
849 wait,
850 on_progress);
851
852 auto end_time = std::chrono::steady_clock::now();
853 auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
854 APPLOG_TRACE("Asset watcher {} took {}ms to watch assets", protocol, elapsed_time.count());
855}
856
857void asset_watcher::unwatch_assets(rtti::context& ctx, const std::string& protocol)
858{
859 auto it = watched_protocols_.find(protocol);
860 if(it == watched_protocols_.end())
861 {
862 return;
863 }
864
865 auto& w = it->second;
866
867 unwatch(w.watchers);
868 w.meta_syncer.unsync();
869 w.cache_syncer.unsync();
870
871 watched_protocols_.erase(it);
872
873 auto& am = ctx.get_cached<asset_manager>();
874 am.unload_group(protocol);
875}
876
878{
879 auto& ts = ctx.get_cached<threader>();
880 return ts.pool->get_jobs_count();
881}
882
883void asset_watcher::recreate_meta_files(rtti::context& ctx, const std::string& protocol)
884{
885 auto& am = ctx.get_cached<asset_manager>();
886 auto assets = am.get_all_assets(protocol);
887
888 std::lock_guard<std::mutex> lock(recreate_meta_files_queue_mutex_);
889 for(auto& asset : assets)
890 {
891
892 auto path = fs::resolve_protocol(asset);
893 recreate_meta_files_queue_[path.string()] = true;
894 }
895
896 for(auto& kvp : recreate_meta_files_queue_)
897 {
898 fs::watcher::touch(fs::resolve_protocol(kvp.first), false);
899 }
900}
901
902} // namespace unravel
A filter that combines include and exclude patterns for file/directory filtering.
void sync(const fs::path &reference_dir, const fs::path &synced_dir, const on_sync_progress_t &on_progress=nullptr)
Start syncing the synced_dir with reference to the reference_dir i.e changes that occur in the refere...
Definition syncer.cpp:148
void set_mapping(const std::string &ref_ext, const std::vector< std::string > &synced_ext, on_entry_created_t on_entry_created, on_entry_modified_t on_entry_modified, on_entry_removed_t on_entry_removed, on_entry_renamed_t on_entry_renamed)
Remaps a specific extension of the reference directory to extensions of the synced directory....
Definition syncer.cpp:78
std::function< void(const std::string &, const rename_pair_t &, const std::vector< rename_pair_t > &)> on_entry_renamed_t
Definition syncer.h:22
void set_directory_mapping(on_entry_created_t on_entry_created, on_entry_modified_t on_entry_modified, on_entry_removed_t on_entry_removed, on_entry_renamed_t on_entry_renamed)
Definition syncer.cpp:94
std::function< void(const std::string &, const fs::path &, const std::vector< fs::path > &)> on_entry_removed_t
Definition syncer.h:21
static void unwatch(std::uint64_t key)
Un-watches a previously registered file or directory.
Definition watcher.cpp:45
static void touch(const fs::path &path, bool recursive, fs::file_time_type time=fs::now())
Sets the last modification time of a file or directory. by default sets the time to the current time.
Definition watcher.cpp:57
static auto watch(const fs::path &path, const pattern_filter &filter, bool recursive, bool initial_list, clock_t::duration poll_interval, notify_callback callback, const std::string &watcher_name="") -> std::uint64_t
Watches a file or directory for modification and call back the specified std::function....
Definition watcher.cpp:27
Manages assets, including loading, unloading, and storage.
auto get_all_assets(const std::string &group) const -> std::vector< std::string >
Gets all assets.
void unload_group(const std::string &group)
Unloads all assets in a specified group.
auto deinit(rtti::context &ctx) -> bool
auto get_pending_jobs_count(rtti::context &ctx) const -> size_t
void watch_assets(rtti::context &ctx, const std::string &protocol, bool wait=false, const on_wait_progress_t &on_progress=nullptr)
void recreate_meta_files(rtti::context &ctx, const std::string &protocol)
auto init(rtti::context &ctx) -> bool
void unwatch_assets(rtti::context &ctx, const std::string &protocol)
std::vector< render_pass_entry > entries
#define APPLOG_WARNING(...)
Definition logging.h:19
#define APPLOG_TRACE_PERF_NAMED_ALLOC(T, name)
Definition logging.h:130
#define APPLOG_TRACE(...)
Definition logging.h:17
texture_job_type type
auto get_suported_formats< gfx::shader >() -> const std::vector< std::string > &
auto get_all_formats() -> const std::vector< std::vector< std::string > > &
auto get_compiled_directory_no_slash(const std::string &prefix={}) -> std::string
auto get_data_directory(const std::string &prefix={}) -> std::string
auto get_type() -> const std::string &
auto get_meta_directory_no_slash(const std::string &prefix={}) -> std::string
auto get_compiled_directory(const std::string &prefix={}) -> std::string
auto get_suported_dependencies_formats< gfx::shader >() -> const std::vector< std::string > &
auto get_meta_directory(const std::string &prefix={}) -> std::string
auto get_suported_formats() -> const std::vector< std::string > &
auto get_data_directory_no_slash(const std::string &prefix={}) -> std::string
auto get_suported_dependencies_formats() -> const std::vector< std::string > &
path extract_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
path reduce_trailing_extensions(const path &_path)
another.
path resolve_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
path replace(const path &_path, const path &_sequence, const path &_new_sequence)
Replacing any occurences of the specified path sequence with another.
path convert_to_protocol(const path &_path)
Oposite of the resolve_protocol this function tries to convert to protocol path from an absolute one.
auto get_renderer_platform_supported_filename_extensions() -> const std::set< std::string > &
auto get_current_renderer_filename_extension() -> const std::string &
auto get_manifest_path(const fs::path &compiled_asset_path) -> fs::path
Generate manifest file path from compiled asset path.
auto is_compiled_format_changed(const fs::path &source_path, const asset_manifest &manifest) -> bool
auto compile< gfx::shader >(asset_manager &am, const fs::path &key, const fs::path &output, uint32_t flags) -> bool
auto is_source_file_changed(const fs::path &source_path, const asset_manifest &manifest) -> bool
Check if source file has changed compared to manifest (source-only)
auto load_manifest(const fs::path &manifest_path, asset_manifest &manifest) -> bool
Load manifest from file.
auto compile(asset_manager &am, const fs::path &key, const fs::path &output_key, uint32_t flags=0) -> bool
void resolve_dependencies(const fs::path &, std::vector< fs::path > &)
void cascade_thumbnail_remove(asset_manager &am, thumbnail_manager &tm, const std::set< hpp::uuid > &removed_uids, const std::set< hpp::uuid > &dependent_uids, bool mass_invalidate_prefabs)
auto find_transitive_loaded_dependents(asset_manager &am, const hpp::uuid &root) -> std::vector< hpp::uuid >
void cascade_thumbnail_regen(asset_manager &am, thumbnail_manager &tm, const hpp::uuid &changed, bool mass_invalidate_prefabs)
auto get_job_name() -> std::string
std::function< void(size_t completed, size_t total, const std::string &current_job)> on_wait_progress_t
Callback invoked after each compilation job completes during a blocking wait.
void load_from_file(const std::string &absolute_path, animation_clip &obj)
void save_to_file(const std::string &absolute_path, const animation_clip &obj)
@ deferred
Register handle only; load triggered on first .get() call.
auto get_cached() -> T &
Definition context.hpp:49
hpp::event< void(rtti::context &, os::event &e)> on_os_event
os events
Definition events.h:35
static void set_needs_recompile(const std::string &protocol, bool now=false)
std::unique_ptr< tpp::thread_pool > pool
Definition threader.h:22
std::vector< GitHubAsset > assets
int priority