4#include "threadpp/thread.h"
44using namespace std::literals;
53auto checking_dependencies_job_name() -> std::string
60auto checking_for_recompilation_job_name() -> std::string
62 return fmt::format(
"Checking for recompilation of {}",
ex::get_type<T>());
67auto get_absolute_source_path(
const fs::path& source_file_path) -> fs::path
71 if(absolute_source.extension() ==
".meta")
73 absolute_source.replace_extension();
75 return absolute_source;
82auto needs_recompilation(
const fs::path& source_file_path,
const fs::path& compiled_output_path) ->
bool
85 if(!fs::exists(compiled_output_path, err) || err)
87 APPLOG_WARNING(
"Compiled output does not exist for {}, recompilation needed", compiled_output_path.string());
91 if(!fs::exists(manifest_path, err) || err)
93 APPLOG_WARNING(
"Manifest does not exist for {}, recompilation needed", compiled_output_path.string());
96 asset_compiler::asset_manifest manifest;
99 APPLOG_WARNING(
"Failed to load manifest for {}, recompilation needed", compiled_output_path.string());
105 APPLOG_WARNING(
"Compiled format changed for {}, recompilation needed", compiled_output_path.string());
109 std::vector<fs::path> deps;
113 APPLOG_WARNING(
"Source file changed for {}, recompilation needed", compiled_output_path.string());
121auto has_depencency(
const fs::path& file,
const fs::path& dep_to_check) ->
bool
123 std::vector<fs::path> dependecies;
125 return std::find(dependecies.begin(), dependecies.end(), dep_to_check) != dependecies.end();
128auto remove_meta_tag(
const fs::path& synced_path) -> fs::path
133auto remove_meta_tag(
const std::vector<fs::path>& synced_paths) -> std::vector<fs::path>
135 std::decay_t<
decltype(synced_paths)> reduced;
136 reduced.reserve(synced_paths.size());
137 for(
const auto& synced_path : synced_paths)
139 reduced.emplace_back(remove_meta_tag(synced_path));
144void unwatch(std::vector<uint64_t>& watchers)
146 for(
const auto&
id : watchers)
153auto get_asset_key(
const fs::path& path) -> std::string
162auto get_meta_key(
const fs::path& path) -> std::string
168 return key +
".meta";
171auto check_files_integrity(
const std::string& key,
const fs::path& entry_path) ->
bool
176 if(!fs::exists(key_path, ec))
178 APPLOG_WARNING(
"{} does not exist. Cleaning up compiled...", key);
179 fs::remove(entry_path, ec);
181 fs::remove(manifest_path, ec);
184 auto meta = get_meta_key(entry_path);
186 if(fs::exists(meta_path, ec))
189 fs::remove(meta_path, ec);
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>();
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)
211 std::set<hpp::uuid> changed;
212 std::set<hpp::uuid> removed;
218 std::set<hpp::uuid> removed_dependents;
222 auto key = get_asset_key(
entry.path);
226 if(
entry.type == fs::file_type::regular)
230 auto asset = am.find_asset<T>(
key);
233 const auto uid = asset.uid();
234 removed.emplace(uid);
237 removed_dependents.insert(deps.begin(), deps.end());
239 am.unload_asset<T>(
key);
242 if constexpr(std::is_same<T, script>::value)
249 auto old_key = get_asset_key(
entry.last_path);
250 am.rename_asset<T>(old_key,
key);
252 if constexpr(std::is_same<T, script>::value)
259 if(check_files_integrity(key,
entry.path))
265 changed.emplace(asset.uid());
269 if constexpr(std::is_same<T, script>::value)
277 if(!changed.empty() || !removed.empty())
279 tpp::invoke(tpp::main_thread::get_id(),
280 [&tm, &em, &am, changed, removed, removed_dependents]()
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>;
306 for(
const auto& uid : changed)
310 if constexpr(std::is_same<T, prefab>::value)
312 auto asset = am.get_asset<T>(uid);
313 em.on_prefab_updated(asset);
320 std::stringstream ss;
321 for(
const auto& pattern : filter.get_include_patterns())
323 ss << pattern.get_pattern() <<
" ";
325 return fs::watcher::watch(watch_dir, filter,
true,
true, 500ms, callback,
"Asset Watcher " + ss.str());
331 auto& am = ctx.get_cached<asset_manager>();
332 auto& ts = ctx.get_cached<threader>();
334 fs::path watch_dir = fs::path(dir).make_preferred();
336 auto callback = [&am, &ts](
const auto&
entries,
bool is_initial_list)
347 if(
entry.type == fs::file_type::regular)
357 auto task = ts.pool->schedule(checking_dependencies_job_name<T>(),
360 auto assets = am.get_assets<T>();
361 for(
const auto& asset :
assets)
363 auto meta = am.get_metadata(asset.uid());
366 if(has_depencency<T>(absolute_path,
entry.path))
377 std::stringstream ss;
378 for(
const auto& pattern : filter.get_include_patterns())
380 ss << pattern.get_pattern() <<
" ";
382 return fs::watcher::watch(watch_dir, filter,
true,
true, 500ms, callback,
"Asset Dependencies Watcher " + ss.str());
395 [&ts, &am](
const std::string& ext,
const auto& ref_path,
const auto& synced_paths,
bool is_initial_listing)
397 auto paths = remove_meta_tag(synced_paths);
399 for(
const auto& output : paths)
401 if(is_initial_listing && !needs_recompilation<T>(ref_path, output))
406 auto key = get_asset_key(output);
407 if(check_files_integrity(key, output))
410 auto task = ts.pool->schedule(job_name,
411 [&am, ref_path, output, job_name]()
432static void watch_synced(
rtti::context& ctx, std::vector<uint64_t>& watchers,
const fs::path& dir)
437 watchers.push_back(watch_id);
451 [&ts, &am](
const std::string& ext,
const auto& ref_path,
const auto& synced_paths,
bool is_initial_listing)
453 auto paths = remove_meta_tag(synced_paths);
460 for(
const auto& output : paths)
462 auto extension = output.extension().string();
464 std::find(std::begin(platform_supported), std::end(platform_supported), extension);
466 if(it == std::end(platform_supported))
471 if(is_initial_listing && !needs_recompilation<gfx::shader>(ref_path, output))
476 auto key = get_asset_key(output);
477 if(check_files_integrity(key, output))
480 tpp::priority::group
priority = high_priority ? tpp::priority::normal() : tpp::
priority::
low();
483 auto task = ts.pool->schedule(job_name,
485 [&am, ref_path, output, job_name]()
495 std::vector<std::string> supported_extensions;
496 supported_extensions.reserve(platform_supported.size());
497 for(
const auto& extension : platform_supported)
499 supported_extensions.push_back(
".asset" + extension);
504 supported_extensions,
513void watch_synced<gfx::shader>(
rtti::context& ctx, std::vector<uint64_t>& watchers,
const fs::path& dir)
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);
528 const auto on_dir_modified =
529 [](
const std::string& ext,
const auto& ,
const auto& ,
bool )
533 const auto on_dir_removed = [](
const std::string& ext,
const auto& ,
const auto& synced_paths)
535 for(
const auto& synced_path : synced_paths)
538 fs::remove_all(synced_path, err);
542 const auto on_dir_renamed = [](
const std::string& ext,
const auto& ,
const auto& synced_paths)
544 for(
const auto& synced_path : synced_paths)
547 fs::rename(synced_path.first, synced_path.second, err);
554 std::vector<uint64_t>& watchers,
556 const fs::path& data_dir,
557 const fs::path& meta_dir,
561 setup_directory(ctx, syncer);
564 const auto on_file_removed = [&am](
const std::string& ext,
const auto& ref_path,
const auto& synced_paths)
566 for(
const auto& synced_path : synced_paths)
569 fs::remove_all(synced_path, err);
571 am.remove_asset_info_for_path(ref_path);
575 const auto on_file_renamed = [](
const std::string& ext,
const auto& ,
const auto& synced_paths)
577 for(
const auto& synced_path : synced_paths)
580 fs::rename(synced_path.first, synced_path.second, err);
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)
587 for(
const auto& synced_path : synced_paths)
591 if(fs::exists(synced_path, err))
596 bool recreate_meta_file =
false;
598 std::lock_guard<std::mutex> lock(recreate_meta_files_queue_mutex_);
599 if(!recreate_meta_files_queue_.empty())
601 auto it = recreate_meta_files_queue_.find(ref_path.string());
602 if(it != recreate_meta_files_queue_.end())
604 recreate_meta_files_queue_.erase(it);
606 recreate_meta_file =
true;
612 if(meta.uid.is_nil() || recreate_meta_file)
614 auto new_meta = am.get_metadata_for_path(ref_path).meta;
616 if(new_meta.uid.is_nil())
619 new_meta = am.generate_metadata(protocol_path);
620 am.remove_asset_info_for_path(ref_path);
623 if(new_meta.uid != meta.uid)
628 meta.uid = am.add_asset_info_for_path(ref_path, meta,
true);
641 for(
const auto&
type : asset_set)
643 syncer.
set_mapping(
type, {
".meta"}, on_file_modified, on_file_modified, on_file_removed, on_file_renamed);
649 auto id = watch_assets_depenencies<gfx::shader>(ctx, data_dir,
fs::pattern_filter(
"*" + dep_ex));
650 watchers.emplace_back(
id);
655 auto id = watch_assets_depenencies<ui_tree>(ctx, data_dir,
fs::pattern_filter(
"*" + dep_ex));
656 watchers.emplace_back(
id);
659 auto on_sync_progress = [on_progress](
size_t completed,
size_t total,
const std::string& current_job)
663 on_progress(completed, total,
"Indexing Assets Metadata " + current_job);
667 syncer.
sync(data_dir, meta_dir, on_sync_progress);
672 APPLOG_TRACE(
"Waiting for jobs to complete... (this may take a while)");
675 ts.pool->wait_all_polling(tpp::priority::category::normal,
676 [&on_progress](
const tpp::thread_pool::progress_info& info)
680 on_progress(info.current_job, info.total_jobs, info.name);
687 std::vector<uint64_t>& watchers,
689 const fs::path& meta_dir,
690 const fs::path& cache_dir,
694 setup_directory(ctx, syncer);
696 auto on_removed = [](
const std::string& ext,
const auto& ,
const auto& synced_paths)
698 for(
const auto& synced_path : synced_paths)
700 auto synced_asset = remove_meta_tag(synced_path);
704 fs::remove_all(manifest_path, err);
705 fs::remove_all(synced_asset, err);
709 auto on_renamed = [](
const std::string& ext,
const auto& ,
const auto& synced_paths)
711 for(
const auto& synced_path : synced_paths)
713 auto synced_old_asset = remove_meta_tag(synced_path.first);
714 auto synced_new_asset = remove_meta_tag(synced_path.second);
718 fs::rename(manifest_old_path, manifest_new_path, err);
719 fs::rename(synced_old_asset, synced_new_asset, err);
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);
737 auto on_sync_progress = [on_progress](
size_t completed,
size_t total,
const std::string& current_job)
741 on_progress(completed, total,
"Indexing Compiled Assets " + current_job);
744 syncer.
sync(meta_dir, cache_dir, on_sync_progress);
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)
756 on_progress(info.current_job, info.total_jobs, info.name);
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);
784void asset_watcher::on_os_event(
rtti::context& ctx, os::event& e)
786 if(e.type == os::events::window)
788 if(e.window.type == os::window_event_id::focus_lost)
790 if(!os::window::is_any_focused())
796 if(
e.window.type == os::window_event_id::focus_gained)
798 if(os::window::is_any_focused())
809 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
812 ev.
on_os_event.connect(sentinel_, 1000,
this, &asset_watcher::on_os_event);
819 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
825 const std::string& protocol,
829 auto start_time = std::chrono::steady_clock::now();
830 auto& w = watched_protocols_[protocol];
836 setup_meta_syncer(ctx,
844 setup_cache_syncer(ctx,
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());
859 auto it = watched_protocols_.find(protocol);
860 if(it == watched_protocols_.end())
865 auto& w = it->second;
868 w.meta_syncer.unsync();
869 w.cache_syncer.unsync();
871 watched_protocols_.erase(it);
880 return ts.
pool->get_jobs_count();
888 std::lock_guard<std::mutex> lock(recreate_meta_files_queue_mutex_);
893 recreate_meta_files_queue_[path.string()] =
true;
896 for(
auto& kvp : recreate_meta_files_queue_)
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...
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....
std::function< void(const std::string &, const rename_pair_t &, const std::vector< rename_pair_t > &)> on_entry_renamed_t
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)
std::function< void(const std::string &, const fs::path &, const std::vector< fs::path > &)> on_entry_removed_t
static void unwatch(std::uint64_t key)
Un-watches a previously registered file or directory.
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.
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....
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)
#define APPLOG_WARNING(...)
#define APPLOG_TRACE_PERF_NAMED_ALLOC(T, name)
#define APPLOG_TRACE(...)
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 ¤t_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.
hpp::event< void(rtti::context &, os::event &e)> on_os_event
os events
static void set_needs_recompile(const std::string &protocol, bool now=false)
std::unique_ptr< tpp::thread_pool > pool
std::vector< GitHubAsset > assets