diff --git a/Lorr/Editor/EditorApp.hh b/Lorr/Editor/EditorApp.hh deleted file mode 100755 index 0c32b068..00000000 --- a/Lorr/Editor/EditorApp.hh +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include "Editor/Window/IWindow.hh" -#include "Editor/Project.hh" - -#include "Engine/Core/Application.hh" - -#include "Engine/Graphics/FrameProfiler.hh" - -namespace led { -struct EditorApp : lr::Application { - static EditorApp &get() { - return dynamic_cast(Application::get()); - } - - std::unique_ptr active_project = nullptr; - ankerl::unordered_dense::map recent_project_infos = {}; - - std::vector> windows = {}; - ls::option dockspace_id = ls::nullopt; - - ankerl::unordered_dense::map editor_assets = {}; - lr::FrameProfiler frame_profiler = {}; - - bool show_profiler = false; - bool show_debug = false; - - auto load_editor_data(this EditorApp &) -> void; - auto save_editor_data(this EditorApp &) -> void; - - auto new_project(this EditorApp &, const fs::path &root_path, const std::string &name) -> std::unique_ptr; - auto open_project(this EditorApp &, const fs::path &path) -> std::unique_ptr; - auto save_project(this EditorApp &, std::unique_ptr &project) -> void; - auto set_active_project(this EditorApp &, std::unique_ptr &&project) -> void; - - auto get_asset_texture(this EditorApp &, lr::Asset *asset) -> lr::Texture *; - auto get_asset_texture(this EditorApp &, lr::AssetType asset_type) -> lr::Texture *; - - auto prepare(this EditorApp &) -> bool; - auto update(this EditorApp &, f64 delta_time) -> bool; - auto render(this EditorApp &, vuk::Format format, vuk::Extent3D extent) -> bool; - auto shutdown(this EditorApp &) -> void; - - template - auto add_window(this EditorApp &self, std::string name, const c8 *icon = nullptr, bool open = true) -> std::pair { - auto window_index = self.windows.size(); - auto icon_name = std::string{}; - if (icon) { - icon_name = fmt::format("{} {}", icon, name); - } else { - icon_name = std::move(name); - } - - auto window = std::make_unique(std::move(icon_name), open); - T *window_ptr = window.get(); - self.windows.push_back(std::move(window)); - return { window_index, window_ptr }; - } - - auto remove_window(this EditorApp &, usize window_id) -> void; - - auto do_super_init([[maybe_unused]] ls::span args) -> bool override { - return true; - } - auto do_shutdown() -> void override { - shutdown(); - } - auto do_prepare() -> bool override { - return prepare(); - } - auto do_update(f64 delta_time) -> bool override { - return update(delta_time); - } - auto do_render(vuk::Format format, vuk::Extent3D extent) -> bool override { - return render(format, extent); - }; -}; -} // namespace led - -// ImGui extension - -namespace ImGui { -bool drag_vec(i32 id, void *data, usize components, ImGuiDataType data_type); -void center_text(std::string_view str); -bool image_button(std::string_view text, ImTextureID texture_id, const ImVec2 &button_size); -void text_sv(std::string_view str); -} // namespace ImGui diff --git a/Lorr/Editor/EditorApp.cc b/Lorr/Editor/EditorModule.cc similarity index 80% rename from Lorr/Editor/EditorApp.cc rename to Lorr/Editor/EditorModule.cc index 8132ef98..48ce6419 100755 --- a/Lorr/Editor/EditorApp.cc +++ b/Lorr/Editor/EditorModule.cc @@ -1,4 +1,4 @@ -#include "Editor/EditorApp.hh" +#include "Editor/EditorModule.hh" #include "Editor/Window/AssetBrowserWindow.hh" #include "Editor/Window/ConsoleWindow.hh" @@ -8,12 +8,16 @@ #include "Editor/Themes.hh" +#include "Engine/Core/App.hh" +#include "Engine/Graphics/ImGuiRenderer.hh" #include "Engine/Memory/Stack.hh" #include "Engine/OS/File.hh" #include "Engine/Util/JsonWriter.hh" #include "Engine/Util/Icons/IconsMaterialDesignIcons.hh" +#include "Engine/Window/Window.hh" +#include #include namespace sj = simdjson; @@ -44,10 +48,11 @@ static auto read_project_file(const fs::path &path) -> ls::option void { +auto EditorModule::load_editor_data(this EditorModule &self) -> void { ZoneScoped; - auto data_file_path = self.asset_man.asset_root_path(lr::AssetType::Root) / "editor.json"; + auto &asset_man = lr::App::mod(); + auto data_file_path = asset_man.asset_root_path(lr::AssetType::Root) / "editor.json"; if (!fs::exists(data_file_path)) { self.save_editor_data(); return; @@ -86,10 +91,11 @@ auto EditorApp::load_editor_data(this EditorApp &self) -> void { } } -auto EditorApp::save_editor_data(this EditorApp &self) -> void { +auto EditorModule::save_editor_data(this EditorModule &self) -> void { ZoneScoped; - auto data_file_path = self.asset_man.asset_root_path(lr::AssetType::Root) / "editor.json"; + auto &asset_man = lr::App::mod(); + auto data_file_path = asset_man.asset_root_path(lr::AssetType::Root) / "editor.json"; lr::JsonWriter json; json.begin_obj(); json["recent_projects"].begin_array(); @@ -107,7 +113,7 @@ auto EditorApp::save_editor_data(this EditorApp &self) -> void { file.close(); } -auto EditorApp::new_project(this EditorApp &self, const fs::path &root_path, const std::string &name) -> std::unique_ptr { +auto EditorModule::new_project(this EditorModule &self, const fs::path &root_path, const std::string &name) -> std::unique_ptr { ZoneScoped; if (!fs::is_directory(root_path)) { @@ -178,7 +184,7 @@ auto EditorApp::new_project(this EditorApp &self, const fs::path &root_path, con return project; } -auto EditorApp::open_project(this EditorApp &self, const fs::path &path) -> std::unique_ptr { +auto EditorModule::open_project(this EditorModule &self, const fs::path &path) -> std::unique_ptr { ZoneScoped; const auto &proj_root_dir = path.parent_path(); @@ -195,56 +201,59 @@ auto EditorApp::open_project(this EditorApp &self, const fs::path &path) -> std: return project; } -auto EditorApp::save_project(this EditorApp &self, std::unique_ptr &project) -> void { +auto EditorModule::save_project(this EditorModule &, std::unique_ptr &project) -> void { ZoneScoped; if (!project->active_scene_uuid) { return; } + auto &asset_man = lr::App::mod(); const auto &scene_uuid = project->active_scene_uuid; - auto *scene_asset = self.asset_man.get_asset(scene_uuid); - self.asset_man.export_asset(scene_uuid, scene_asset->path); + auto *scene_asset = asset_man.get_asset(scene_uuid); + asset_man.export_asset(scene_uuid, scene_asset->path); } -auto EditorApp::set_active_project(this EditorApp &self, std::unique_ptr &&project) -> void { +auto EditorModule::set_active_project(this EditorModule &self, std::unique_ptr &&project) -> void { ZoneScoped; self.active_project = std::move(project); } -auto EditorApp::get_asset_texture(this EditorApp &self, lr::Asset *asset) -> lr::Texture * { +auto EditorModule::get_asset_texture(this EditorModule &self, lr::Asset *asset) -> lr::Texture * { ZoneScoped; return self.get_asset_texture(asset->type); } -auto EditorApp::get_asset_texture(this EditorApp &self, lr::AssetType asset_type) -> lr::Texture * { +auto EditorModule::get_asset_texture(this EditorModule &self, lr::AssetType asset_type) -> lr::Texture * { ZoneScoped; + auto &asset_man = lr::App::mod(); switch (asset_type) { case lr::AssetType::Model: - return self.asset_man.get_texture(self.editor_assets["model"]); + return asset_man.get_texture(self.editor_assets["model"]); case lr::AssetType::Texture: - return self.asset_man.get_texture(self.editor_assets["texture"]); + return asset_man.get_texture(self.editor_assets["texture"]); case lr::AssetType::Scene: - return self.asset_man.get_texture(self.editor_assets["scene"]); + return asset_man.get_texture(self.editor_assets["scene"]); case lr::AssetType::Directory: - return self.asset_man.get_texture(self.editor_assets["dir"]); + return asset_man.get_texture(self.editor_assets["dir"]); default: - return self.asset_man.get_texture(self.editor_assets["file"]); + return asset_man.get_texture(self.editor_assets["file"]); } } -bool EditorApp::prepare(this EditorApp &self) { +bool EditorModule::init(this EditorModule &self) { ZoneScoped; self.load_editor_data(); Theme::dark(); auto add_texture = [&self](std::string name, const fs::path &path) { - auto asset_uuid = self.asset_man.create_asset(lr::AssetType::Texture, self.asset_man.asset_root_path(lr::AssetType::Root) / "editor" / path); - self.asset_man.load_texture(asset_uuid); + auto &asset_man = lr::App::mod(); + auto asset_uuid = asset_man.create_asset(lr::AssetType::Texture, asset_man.asset_root_path(lr::AssetType::Root) / "editor" / path); + asset_man.load_texture(asset_uuid); self.editor_assets.emplace(name, asset_uuid); }; @@ -257,23 +266,37 @@ bool EditorApp::prepare(this EditorApp &self) { return true; } -bool EditorApp::update(this EditorApp &self, f64 delta_time) { +bool EditorModule::update(this EditorModule &self, f64 delta_time) { ZoneScoped; + auto &asset_man = lr::App::mod(); + auto &device = lr::App::mod(); + auto &window = lr::App::mod(); + auto &imgui_renderer = lr::App::mod(); + + auto swapchain_attachment = device.new_frame(window.swap_chain.value()); + swapchain_attachment = vuk::clear_image(std::move(swapchain_attachment), vuk::Black); + imgui_renderer.begin_frame(delta_time, swapchain_attachment->extent); + if (self.active_project) { const auto &active_scene_uuid = self.active_project->active_scene_uuid; if (active_scene_uuid) { - auto *active_scene = self.asset_man.get_scene(active_scene_uuid); + auto *active_scene = asset_man.get_scene(active_scene_uuid); active_scene->tick(static_cast(delta_time)); } } - self.frame_profiler.measure(&self.device, delta_time); + self.render(swapchain_attachment->format, swapchain_attachment->extent); + + self.frame_profiler.measure(&device, delta_time); + + swapchain_attachment = imgui_renderer.end_frame(std::move(swapchain_attachment)); + device.end_frame(std::move(swapchain_attachment)); return true; } -static auto setup_dockspace(EditorApp &self) -> void { +static auto setup_dockspace(EditorModule &self) -> void { ZoneScoped; auto [asset_browser_panel_id, asset_browser_panel] = self.add_window("Asset Browser", ICON_MDI_IMAGE_MULTIPLE); @@ -307,7 +330,7 @@ static auto setup_dockspace(EditorApp &self) -> void { ImGui::DockBuilderFinish(dockspace_id_tmp); } -static auto draw_menu_bar(EditorApp &self) -> void { +static auto draw_menu_bar(EditorModule &self) -> void { ZoneScoped; if (ImGui::BeginMenuBar()) { @@ -319,7 +342,7 @@ static auto draw_menu_bar(EditorApp &self) -> void { ImGui::Separator(); if (ImGui::MenuItem("Exit")) { - self.should_close = true; + lr::App::close(); } ImGui::EndMenu(); @@ -338,9 +361,10 @@ static auto draw_menu_bar(EditorApp &self) -> void { } } -static auto draw_welcome_popup(EditorApp &self) -> void { +static auto draw_welcome_popup(EditorModule &self) -> void { ZoneScoped; + auto &window = lr::App::mod(); ImGui::SetNextWindowSize({ 480.0f, 350.0f }, ImGuiCond_Appearing); constexpr auto popup_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings; if (ImGui::BeginPopupModal("###welcome", nullptr, popup_flags)) { @@ -372,24 +396,23 @@ static auto draw_welcome_popup(EditorApp &self) -> void { ImGui::SeparatorText(""); if (ImGui::Button("Import...", { -1.0f, 35.0f })) { - self.window.show_dialog( + window.show_dialog( { .kind = lr::DialogKind::OpenFile, .user_data = &self, .title = "Select a project...", - .callback = - [](void *user_data, const c8 *const *files, i32) { - if (!files || !*files) { - return; - } - - auto *app = static_cast(user_data); - auto lrporj_file = fs::path(files[0]); - auto project_info = read_project_file(lrporj_file); - if (project_info.has_value()) { - app->recent_project_infos.emplace(lrporj_file, project_info.value()); - app->save_editor_data(); - } - } } + .callback = [](void *user_data, const c8 *const *files, i32) { + if (!files || !*files) { + return; + } + + auto *app = static_cast(user_data); + auto lrporj_file = fs::path(files[0]); + auto project_info = read_project_file(lrporj_file); + if (project_info.has_value()) { + app->recent_project_infos.emplace(lrporj_file, project_info.value()); + app->save_editor_data(); + } + } } ); } @@ -409,17 +432,14 @@ static auto draw_welcome_popup(EditorApp &self) -> void { ImGui::InputTextWithHint("##project_dir", "/path/to/new/project", &project_dir); ImGui::SameLine(); if (ImGui::Button(ICON_MDI_FOLDER, { -1.0f, 0.0f })) { - self.window.show_dialog( - { .kind = lr::DialogKind::OpenFolder, - .title = "Select a directory...", - .callback = - [](void *, const c8 *const *files, i32) { - if (!files || !*files) { - return; - } - - project_dir = files[0]; - } } + window.show_dialog( + { .kind = lr::DialogKind::OpenFolder, .title = "Select a directory...", .callback = [](void *, const c8 *const *files, i32) { + if (!files || !*files) { + return; + } + + project_dir = files[0]; + } } ); } @@ -452,7 +472,7 @@ static auto draw_welcome_popup(EditorApp &self) -> void { } } -static auto draw_profiler(EditorApp &self) -> void { +static auto draw_profiler(EditorModule &self) -> void { ZoneScoped; if (!self.show_profiler) { @@ -487,7 +507,7 @@ static auto draw_profiler(EditorApp &self) -> void { ImGui::End(); } -auto EditorApp::render(this EditorApp &self, vuk::Format format, vuk::Extent3D extent) -> bool { +auto EditorModule::render(this EditorModule &self, vuk::Format format, vuk::Extent3D extent) -> bool { ZoneScoped; auto *viewport = ImGui::GetMainViewport(); @@ -541,18 +561,19 @@ auto EditorApp::render(this EditorApp &self, vuk::Format format, vuk::Extent3D e return true; } -auto EditorApp::shutdown(this EditorApp &self) -> void { +auto EditorModule::destroy(this EditorModule &self) -> void { ZoneScoped; + auto &asset_man = lr::App::mod(); for (const auto &[name, uuid] : self.editor_assets) { - self.asset_man.unload_asset(uuid); + asset_man.unload_asset(uuid); } self.windows.clear(); self.active_project.reset(); } -auto EditorApp::remove_window(this EditorApp &self, usize window_id) -> void { +auto EditorModule::remove_window(this EditorModule &self, usize window_id) -> void { ZoneScoped; self.windows.erase(self.windows.begin() + static_cast>(window_id)); diff --git a/Lorr/Editor/EditorModule.hh b/Lorr/Editor/EditorModule.hh new file mode 100755 index 00000000..62a367ba --- /dev/null +++ b/Lorr/Editor/EditorModule.hh @@ -0,0 +1,68 @@ +#pragma once + +#include "Editor/Project.hh" +#include "Editor/Window/IWindow.hh" + +#include "Engine/Asset/Asset.hh" + +#include "Engine/Graphics/FrameProfiler.hh" + +namespace led { +struct EditorModule { + constexpr static auto MODULE_NAME = "Editor"; + + std::unique_ptr active_project = nullptr; + ankerl::unordered_dense::map recent_project_infos = {}; + + std::vector> windows = {}; + ls::option dockspace_id = ls::nullopt; + + ankerl::unordered_dense::map editor_assets = {}; + lr::FrameProfiler frame_profiler = {}; + + bool show_profiler = false; + bool show_debug = false; + + auto load_editor_data(this EditorModule &) -> void; + auto save_editor_data(this EditorModule &) -> void; + + auto new_project(this EditorModule &, const fs::path &root_path, const std::string &name) -> std::unique_ptr; + auto open_project(this EditorModule &, const fs::path &path) -> std::unique_ptr; + auto save_project(this EditorModule &, std::unique_ptr &project) -> void; + auto set_active_project(this EditorModule &, std::unique_ptr &&project) -> void; + + auto get_asset_texture(this EditorModule &, lr::Asset *asset) -> lr::Texture *; + auto get_asset_texture(this EditorModule &, lr::AssetType asset_type) -> lr::Texture *; + + auto init(this EditorModule &) -> bool; + auto update(this EditorModule &, f64 delta_time) -> bool; + auto render(this EditorModule &, vuk::Format format, vuk::Extent3D extent) -> bool; + auto destroy(this EditorModule &) -> void; + + template + auto add_window(this EditorModule &self, std::string name, const c8 *icon = nullptr, bool open = true) -> std::pair { + auto window_index = self.windows.size(); + auto icon_name = std::string{}; + if (icon) { + icon_name = fmt::format("{} {}", icon, name); + } else { + icon_name = std::move(name); + } + + auto window = std::make_unique(std::move(icon_name), open); + T *window_ptr = window.get(); + self.windows.push_back(std::move(window)); + return { window_index, window_ptr }; + } + + auto remove_window(this EditorModule &, usize window_id) -> void; +}; +} // namespace led + +// ImGui extension +namespace ImGui { +bool drag_vec(i32 id, void *data, usize components, ImGuiDataType data_type); +void center_text(std::string_view str); +bool image_button(std::string_view text, ImTextureID texture_id, const ImVec2 &button_size); +void text_sv(std::string_view str); +} // namespace ImGui diff --git a/Lorr/Editor/Project.cc b/Lorr/Editor/Project.cc index e6191a46..0dedb83d 100644 --- a/Lorr/Editor/Project.cc +++ b/Lorr/Editor/Project.cc @@ -1,6 +1,7 @@ #include "Editor/Project.hh" -#include "Editor/EditorApp.hh" +#include "Engine/Asset/Asset.hh" +#include "Engine/Core/App.hh" namespace led { Project::Project(fs::path root_path_, const ProjectFileInfo &info_) : root_dir(std::move(root_path_)), name(info_.name) {} @@ -8,27 +9,28 @@ Project::Project(fs::path root_path_, std::string name_) : root_dir(std::move(ro Project::~Project() { ZoneScoped; - auto &app = EditorApp::get(); - if (active_scene_uuid && app.asset_man.get_asset(active_scene_uuid)) { - app.asset_man.unload_scene(active_scene_uuid); + auto &asset_man = lr::App::mod(); + if (active_scene_uuid && asset_man.get_asset(active_scene_uuid)) { + asset_man.unload_scene(active_scene_uuid); } - app.scene_renderer.cleanup(); + // app.scene_renderer.cleanup(); } auto Project::set_active_scene(this Project &self, const lr::UUID &scene_uuid) -> bool { ZoneScoped; - auto &app = EditorApp::get(); + auto &asset_man = lr::App::mod(); + if (self.active_scene_uuid) { - app.asset_man.unload_scene(self.active_scene_uuid); + asset_man.unload_scene(self.active_scene_uuid); } - if (!app.asset_man.load_scene(scene_uuid)) { + if (!asset_man.load_scene(scene_uuid)) { return false; } - app.scene_renderer.cleanup(); + // app.scene_renderer.cleanup(); self.selected_entity = {}; self.active_scene_uuid = scene_uuid; diff --git a/Lorr/Editor/Window/AssetBrowserWindow.cc b/Lorr/Editor/Window/AssetBrowserWindow.cc index 4463e225..e791e6e3 100755 --- a/Lorr/Editor/Window/AssetBrowserWindow.cc +++ b/Lorr/Editor/Window/AssetBrowserWindow.cc @@ -1,6 +1,10 @@ #include "Editor/Window/AssetBrowserWindow.hh" -#include "Editor/EditorApp.hh" +#include "EditorModule.hh" +#include "Engine/Asset/Asset.hh" +#include "Engine/Core/App.hh" + +#include "Engine/Graphics/ImGuiRenderer.hh" #include "Engine/Util/Icons/IconsMaterialDesignIcons.hh" namespace led { @@ -56,10 +60,10 @@ auto populate_directory(AssetDirectory *dir, const AssetDirectoryCallbacks &call AssetDirectory::AssetDirectory(fs::path path_, AssetDirectory *parent_) : path(std::move(path_)), parent(parent_) {} AssetDirectory::~AssetDirectory() { - auto &app = lr::Application::get(); + auto &asset_man = lr::App::mod(); for (const auto &asset_uuid : this->asset_uuids) { - if (app.asset_man.get_asset(asset_uuid)) { - app.asset_man.delete_asset(asset_uuid); + if (asset_man.get_asset(asset_uuid)) { + asset_man.delete_asset(asset_uuid); } } } @@ -78,8 +82,8 @@ auto AssetDirectory::add_subdir(this AssetDirectory &self, std::unique_ptr lr::UUID { - auto &app = lr::Application::get(); - auto asset_uuid = app.asset_man.import_asset(path); + auto &asset_man = lr::App::mod(); + auto asset_uuid = asset_man.import_asset(path); if (!asset_uuid) { return lr::UUID(nullptr); } @@ -181,7 +185,9 @@ auto AssetBrowserWindow::find_directory(this AssetBrowserWindow &self, const fs: } static auto draw_dir_contents(AssetBrowserWindow &self) -> void { - auto &app = EditorApp::get(); + auto &asset_man = lr::App::mod(); + auto &imgui_renderer = lr::App::mod(); + auto &editor = lr::App::mod(); i32 table_flags = ImGuiTableFlags_ContextMenuInBody; table_flags |= ImGuiTableFlags_ScrollY; @@ -197,8 +203,8 @@ static auto draw_dir_contents(AssetBrowserWindow &self) -> void { bool open_create_dir_popup = false; bool open_create_scene_popup = false; - auto *dir_texture = app.get_asset_texture(lr::AssetType::Directory); - auto dir_image = app.imgui_renderer.add_image(dir_texture->image_view); + auto *dir_texture = editor.get_asset_texture(lr::AssetType::Directory); + auto dir_image = imgui_renderer.add_image(dir_texture->image_view); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { padding, padding }); if (tile_count && ImGui::BeginTable("asset_browser", tile_count, table_flags)) { @@ -227,14 +233,14 @@ static auto draw_dir_contents(AssetBrowserWindow &self) -> void { for (const auto &uuid : self.current_dir->asset_uuids) { ImGui::TableNextColumn(); - auto *asset = app.asset_man.get_asset(uuid); + auto *asset = asset_man.get_asset(uuid); if (!asset) { continue; } const auto &file_name = asset->path.filename().string(); - auto *asset_texture = app.get_asset_texture(asset); - auto asset_image = app.imgui_renderer.add_image(asset_texture->image_view); + auto *asset_texture = editor.get_asset_texture(asset); + auto asset_image = imgui_renderer.add_image(asset_texture->image_view); ImGui::image_button(file_name, asset_image, button_size); if (ImGui::BeginDragDropSource()) { ImGui::SetDragDropPayload("ASSET_BY_UUID", &asset->uuid, sizeof(lr::UUID)); @@ -326,11 +332,11 @@ static auto draw_dir_contents(AssetBrowserWindow &self) -> void { auto new_scene_path = self.current_dir->path / (new_scene_name + ".json"); if (!fs::exists(new_scene_path)) { new_scene_err = false; - auto new_scene_uuid = app.asset_man.create_asset(lr::AssetType::Scene, new_scene_path); - new_scene_err |= !app.asset_man.init_new_scene(new_scene_uuid, new_scene_name); - new_scene_err |= !app.asset_man.export_asset(new_scene_uuid, new_scene_path); + auto new_scene_uuid = asset_man.create_asset(lr::AssetType::Scene, new_scene_path); + new_scene_err |= !asset_man.init_new_scene(new_scene_uuid, new_scene_name); + new_scene_err |= !asset_man.export_asset(new_scene_uuid, new_scene_path); if (!new_scene_err) { - app.asset_man.unload_scene(new_scene_uuid); + asset_man.unload_scene(new_scene_uuid); self.current_dir->refresh(); ImGui::CloseCurrentPopup(); } @@ -445,17 +451,17 @@ static auto draw_file_paths(AssetBrowserWindow &self) -> void { void AssetBrowserWindow::render(this AssetBrowserWindow &self) { ZoneScoped; - auto &app = EditorApp::get(); + auto &editor = lr::App::mod(); // If we just opened the project and home dir is missing - if (app.active_project && self.home_dir == nullptr) { - auto new_home_dir = self.add_directory(app.active_project->root_dir); + if (editor.active_project && self.home_dir == nullptr) { + auto new_home_dir = self.add_directory(editor.active_project->root_dir); self.current_dir = new_home_dir.get(); self.home_dir = std::move(new_home_dir); } if (ImGui::Begin(self.name.data(), nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { - if (app.active_project) { + if (editor.active_project) { draw_file_paths(self); } @@ -465,11 +471,11 @@ void AssetBrowserWindow::render(this AssetBrowserWindow &self) { ImGui::TableNextRow(); ImGui::TableNextColumn(); - if (app.active_project) { + if (editor.active_project) { draw_project_tree(self); } ImGui::TableNextColumn(); - if (app.active_project) { + if (editor.active_project) { draw_dir_contents(self); } diff --git a/Lorr/Editor/Window/InspectorWindow.cc b/Lorr/Editor/Window/InspectorWindow.cc index 97499d6b..80b85782 100755 --- a/Lorr/Editor/Window/InspectorWindow.cc +++ b/Lorr/Editor/Window/InspectorWindow.cc @@ -1,16 +1,19 @@ #include "Editor/Window/InspectorWindow.hh" -#include "Editor/EditorApp.hh" +#include "Editor/EditorModule.hh" #include "Engine/Memory/Stack.hh" #include "Engine/Scene/ECSModule/ComponentWrapper.hh" #include "Engine/Util/Icons/IconsMaterialDesignIcons.hh" +#include "Engine/Asset/Asset.hh" +#include "Engine/Core/App.hh" + namespace led { auto inspect_asset(lr::UUID &uuid) -> bool { lr::memory::ScopedStack stack; - auto &app = lr::Application::get(); - auto *asset = app.asset_man.get_asset(uuid); + auto &asset_man = lr::App::mod(); + auto *asset = asset_man.get_asset(uuid); if (!asset) { ImGui::TextUnformatted("Drop a model here."); uuid = {}; @@ -18,17 +21,19 @@ auto inspect_asset(lr::UUID &uuid) -> bool { const auto &asset_name = asset->path.filename(); ImGui::TextUnformatted(stack.format_char("Name: {}", asset_name)); ImGui::TextUnformatted(stack.format_char("UUID: {}", uuid.str())); - ImGui::TextUnformatted(stack.format_char("Type: {}", app.asset_man.to_asset_type_sv(asset->type))); + ImGui::TextUnformatted(stack.format_char("Type: {}", asset_man.to_asset_type_sv(asset->type))); } return false; } static auto draw_inspector(InspectorWindow &) -> void { - auto &app = led::EditorApp::get(); - auto &active_project = *app.active_project; + auto &editor = lr::App::mod(); + auto &asset_man = lr::App::mod(); + + auto &active_project = *editor.active_project; auto &active_scene_uuid = active_project.active_scene_uuid; - auto *active_scene = app.asset_man.get_scene(active_scene_uuid); + auto *active_scene = asset_man.get_scene(active_scene_uuid); auto &selected_entity = active_project.selected_entity; auto region = ImGui::GetContentRegionAvail(); @@ -105,6 +110,7 @@ static auto draw_inspector(InspectorWindow &) -> void { std::visit( ls::match{ [](const auto &) {}, + [&](bool *v) { component_modified |= ImGui::Checkbox("", v); }, [&](f32 *v) { component_modified |= ImGui::drag_vec(0, v, 1, ImGuiDataType_Float); }, [&](i32 *v) { component_modified |= ImGui::drag_vec(0, v, 1, ImGuiDataType_S32); }, [&](u32 *v) { component_modified |= ImGui::drag_vec(0, v, 1, ImGuiDataType_U32); }, @@ -166,7 +172,7 @@ static auto draw_inspector(InspectorWindow &) -> void { if (auto *component_uuid = std::get_if(&member)) { const auto &uuid = **component_uuid; if (uuid) { - app.asset_man.unload_asset(uuid); + asset_man.unload_asset(uuid); } } }); @@ -180,8 +186,9 @@ InspectorWindow::InspectorWindow(std::string name_, bool open_) : IWindow(std::m void InspectorWindow::render(this InspectorWindow &self) { if (ImGui::Begin(self.name.data())) { - auto &app = EditorApp::get(); - if (app.active_project && app.active_project->active_scene_uuid && app.active_project->selected_entity) { + auto &editor = lr::App::mod(); + + if (editor.active_project && editor.active_project->active_scene_uuid && editor.active_project->selected_entity) { draw_inspector(self); } } diff --git a/Lorr/Editor/Window/SceneBrowserWindow.cc b/Lorr/Editor/Window/SceneBrowserWindow.cc index 5bffef11..72140321 100755 --- a/Lorr/Editor/Window/SceneBrowserWindow.cc +++ b/Lorr/Editor/Window/SceneBrowserWindow.cc @@ -1,20 +1,24 @@ #include "Editor/Window/SceneBrowserWindow.hh" -#include "Editor/EditorApp.hh" +#include "Editor/EditorModule.hh" #include "Engine/Memory/Stack.hh" #include "Engine/Scene/ECSModule/Core.hh" #include "Engine/Util/Icons/IconsMaterialDesignIcons.hh" +#include "Engine/Asset/Asset.hh" +#include "Engine/Core/App.hh" + namespace led { static auto draw_children(SceneBrowserWindow &self, flecs::entity root) -> void { ZoneScoped; - auto &app = EditorApp::get(); - auto &active_project = *app.active_project; + auto &asset_man = lr::App::mod(); + auto &editor = lr::App::mod(); + auto &active_project = *editor.active_project; auto &active_scene_uuid = active_project.active_scene_uuid; - auto *active_scene = app.asset_man.get_scene(active_scene_uuid); + auto *active_scene = asset_man.get_scene(active_scene_uuid); auto &selected_entity = active_project.selected_entity; auto &world = active_scene->get_world(); @@ -70,10 +74,11 @@ static auto draw_children(SceneBrowserWindow &self, flecs::entity root) -> void static auto draw_hierarchy(SceneBrowserWindow &self) -> void { ZoneScoped; - auto &app = EditorApp::get(); - auto &active_project = *app.active_project; + auto &asset_man = lr::App::mod(); + auto &editor = lr::App::mod(); + auto &active_project = *editor.active_project; auto &active_scene_uuid = active_project.active_scene_uuid; - auto *active_scene = app.asset_man.get_scene(active_scene_uuid); + auto *active_scene = asset_man.get_scene(active_scene_uuid); draw_children(self, active_scene->get_root()); @@ -89,11 +94,10 @@ static auto draw_hierarchy(SceneBrowserWindow &self) -> void { ImGui::Separator(); - if (ImGui::MenuItem("Directional Light")) { + if (ImGui::MenuItem("Environment")) { auto created_entity = active_scene->create_entity(); created_entity.set({}); - created_entity.set({}); - created_entity.set({}); + created_entity.set({}); created_entity.child_of(active_scene->get_root()); } @@ -132,8 +136,8 @@ void SceneBrowserWindow::render(this SceneBrowserWindow &self) { ImGui::TableHeadersRow(); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 0, 0 }); - auto &app = EditorApp::get(); - if (app.active_project && app.active_project->active_scene_uuid) { + auto &editor = lr::App::mod(); + if (editor.active_project && editor.active_project->active_scene_uuid) { draw_hierarchy(self); } ImGui::PopStyleVar(); diff --git a/Lorr/Editor/Window/ViewportWindow.cc b/Lorr/Editor/Window/ViewportWindow.cc index 579b7586..eeca6933 100755 --- a/Lorr/Editor/Window/ViewportWindow.cc +++ b/Lorr/Editor/Window/ViewportWindow.cc @@ -1,22 +1,27 @@ #include "Editor/Window/ViewportWindow.hh" -#include "Editor/EditorApp.hh" +#include "Editor/EditorModule.hh" +#include "Engine/Graphics/ImGuiRenderer.hh" #include "Engine/Scene/ECSModule/Core.hh" #include "Engine/Util/Icons/IconsMaterialDesignIcons.hh" +#include "Engine/Asset/Asset.hh" +#include "Engine/Core/App.hh" + #include #include namespace led { static auto on_drop(ViewportWindow &) -> void { - auto &app = EditorApp::get(); - auto &active_project = *app.active_project; - auto *active_scene = app.asset_man.get_scene(active_project.active_scene_uuid); + auto &asset_man = lr::App::mod(); + auto &editor = lr::App::mod(); + auto &active_project = *editor.active_project; + auto *active_scene = asset_man.get_scene(active_project.active_scene_uuid); if (const auto *asset_payload = ImGui::AcceptDragDropPayload("ASSET_BY_UUID")) { auto *uuid = static_cast(asset_payload->Data); - auto *asset = app.asset_man.get_asset(*uuid); + auto *asset = asset_man.get_asset(*uuid); switch (asset->type) { case lr::AssetType::Scene: { active_project.set_active_scene(*uuid); @@ -32,9 +37,10 @@ static auto on_drop(ViewportWindow &) -> void { } static auto draw_tools(ViewportWindow &self) -> void { - auto &app = EditorApp::get(); - auto &active_project = *app.active_project; - auto *active_scene = app.asset_man.get_scene(active_project.active_scene_uuid); + auto &asset_man = lr::App::mod(); + auto &editor = lr::App::mod(); + auto &active_project = *editor.active_project; + auto *active_scene = asset_man.get_scene(active_project.active_scene_uuid); auto *current_window = ImGui::GetCurrentWindow(); auto window_rect = current_window->InnerRect; @@ -97,8 +103,8 @@ static auto draw_tools(ViewportWindow &self) -> void { ImGui::SameLine(right_align_offset); if (ImGui::Button(ICON_MDI_CHART_BAR)) { - app.show_profiler = !app.show_profiler; - app.frame_profiler.reset(); + editor.show_profiler = !editor.show_profiler; + editor.frame_profiler.reset(); } if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { ImGui::SetTooltip("Frame Profiler"); @@ -107,7 +113,7 @@ static auto draw_tools(ViewportWindow &self) -> void { ImGui::SameLine(right_align_offset); if (ImGui::Button(ICON_MDI_BUG)) { - app.show_debug = !app.show_debug; + editor.show_debug = !editor.show_debug; } if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { ImGui::SetTooltip("Debug"); @@ -142,20 +148,24 @@ static auto draw_tools(ViewportWindow &self) -> void { ImGui::EndPopup(); } - if (app.show_debug) { + if (editor.show_debug) { auto &cull_flags = reinterpret_cast(active_scene->get_cull_flags()); ImGui::CheckboxFlags("Cull Meshlet Frustum", &cull_flags, std::to_underlying(lr::GPU::CullFlags::MeshletFrustum)); ImGui::CheckboxFlags("Cull Triangle Back Face", &cull_flags, std::to_underlying(lr::GPU::CullFlags::TriangleBackFace)); ImGui::CheckboxFlags("Cull Micro Triangles", &cull_flags, std::to_underlying(lr::GPU::CullFlags::MicroTriangles)); ImGui::CheckboxFlags("Cull Occlusion", &cull_flags, std::to_underlying(lr::GPU::CullFlags::Occlusion)); - ImGui::Checkbox("Debug Lines", &app.scene_renderer.debug_lines); + // ImGui::Checkbox("Debug Lines", &editor.scene_renderer.debug_lines); } } static auto draw_viewport(ViewportWindow &self, vuk::Format format, vuk::Extent3D) -> void { - auto &app = EditorApp::get(); - auto &active_project = *app.active_project; - auto *active_scene = app.asset_man.get_scene(active_project.active_scene_uuid); + auto &asset_man = lr::App::mod(); + auto &editor = lr::App::mod(); + auto &scene_renderer = lr::App::mod(); + auto &imgui_renderer = lr::App::mod(); + + auto &active_project = *editor.active_project; + auto *active_scene = asset_man.get_scene(active_project.active_scene_uuid); auto &selected_entity = active_project.selected_entity; auto editor_camera = active_scene->get_editor_camera(); @@ -168,6 +178,7 @@ static auto draw_viewport(ViewportWindow &self, vuk::Format format, vuk::Extent3 auto *camera = editor_camera.get_mut(); auto *camera_transform = editor_camera.get_mut(); + camera->resolution = { window_size.x, window_size.y }; camera->aspect_ratio = window_size.x / window_size.y; ImGuizmo::SetDrawlist(); @@ -188,14 +199,27 @@ static auto draw_viewport(ViewportWindow &self, vuk::Format format, vuk::Extent3 auto mouse_pos_rel = ImVec2(mouse_pos.x - window_pos.x, mouse_pos.y - window_pos.y); requested_texel_transform.emplace(glm::uvec2(mouse_pos_rel.x, mouse_pos_rel.y)); } - auto scene_render_info = lr::SceneRenderInfo{ + + auto prepared_frame = active_scene->prepare_frame(scene_renderer); + + auto viewport_attachment_info = vuk::ImageAttachment{ + .image_type = vuk::ImageType::e2D, + .usage = vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eColorAttachment, + .extent = { .width = static_cast(window_size.x), .height = static_cast(window_size.y), .depth = 1 }, .format = format, - .extent = vuk::Extent3D(static_cast(window_size.x), static_cast(window_size.y), 1), + .sample_count = vuk::Samples::e1, + .view_type = vuk::ImageViewType::e2D, + .level_count = 1, + .layer_count = 1, + }; + auto viewport_attachment = vuk::declare_ia("viewport", viewport_attachment_info); + auto scene_render_info = lr::SceneRenderInfo{ .delta_time = ImGui::GetIO().DeltaTime, + .cull_flags = active_scene->get_cull_flags(), .picking_texel = requested_texel_transform, }; - auto scene_render_result = active_scene->render(app.scene_renderer, scene_render_info); - auto scene_render_image_idx = app.imgui_renderer.add_image(std::move(scene_render_result)); + viewport_attachment = scene_renderer.render(std::move(viewport_attachment), scene_render_info, prepared_frame); + auto scene_render_image_idx = imgui_renderer.add_image(std::move(viewport_attachment)); ImGui::Image(scene_render_image_idx, work_area_size); if (update_visible_entity) { @@ -323,8 +347,8 @@ ViewportWindow::ViewportWindow(std::string name_, bool open_) : IWindow(std::mov } auto ViewportWindow::render(this ViewportWindow &self, vuk::Format format, vuk::Extent3D extent) -> void { - auto &app = EditorApp::get(); - const auto should_render = app.active_project && app.active_project->active_scene_uuid; + auto &editor = lr::App::mod(); + const auto should_render = editor.active_project && editor.active_project->active_scene_uuid; ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0, 0.0)); if (ImGui::Begin(self.name.data())) { @@ -334,7 +358,7 @@ auto ViewportWindow::render(this ViewportWindow &self, vuk::Format format, vuk:: } auto *current_window = ImGui::GetCurrentWindow(); - if (app.active_project && ImGui::BeginDragDropTargetCustom(current_window->InnerRect, ImGui::GetID("##viewport_drop_target"))) { + if (editor.active_project && ImGui::BeginDragDropTargetCustom(current_window->InnerRect, ImGui::GetID("##viewport_drop_target"))) { on_drop(self); ImGui::EndDragDropTarget(); } diff --git a/Lorr/Editor/main.cc b/Lorr/Editor/main.cc index 2243da91..682e7f66 100755 --- a/Lorr/Editor/main.cc +++ b/Lorr/Editor/main.cc @@ -1,18 +1,25 @@ -#include "EditorApp.hh" +#include "Editor/EditorModule.hh" + +#include "Engine/Core/App.hh" +#include "Engine/Graphics/ImGuiRenderer.hh" #include "Engine/Window/Window.hh" -static led::EditorApp app; +i32 main(i32, c8 **) { + ZoneScoped; -lr::Application &lr::Application::get() { - return app; -} + lr::Window::init_sdl(); + auto primary_display = lr::Window::display_at(0).value(); -i32 main(i32 argc, c8 **argv) { - ZoneScoped; + lr::AppBuilder() // + .module(3) + .module( + lr::WindowInfo{ .title = "Lorr Editor", .width = 1720, .height = 880, .flags = lr::WindowFlag::Centered | lr::WindowFlag::Resizable } + ) + .module() + .module() + .module() + .module() + .build(8, "editor.log"); - app.init(lr::ApplicationInfo{ - .args = { argv, static_cast(argc) }, - .window_info = { .title = "Lorr Editor", .width = 1720, .height = 880, .flags = lr::WindowFlag::Centered, }, - }); return 0; } diff --git a/Lorr/Engine/Asset/Asset.cc b/Lorr/Engine/Asset/Asset.cc index 562b69fc..dd430904 100755 --- a/Lorr/Engine/Asset/Asset.cc +++ b/Lorr/Engine/Asset/Asset.cc @@ -4,7 +4,7 @@ #include "Engine/Asset/ParserKTX2.hh" #include "Engine/Asset/ParserSTB.hh" -#include "Engine/Core/Application.hh" +#include "Engine/Core/App.hh" #include "Engine/Core/Logger.hh" #include "Engine/Graphics/VulkanDevice.hh" @@ -112,62 +112,35 @@ auto end_asset_meta(JsonWriter &json, const fs::path &path) -> bool { return true; } -template<> -struct Handle::Impl { - Device *device = nullptr; - fs::path root_path = fs::current_path(); - AssetRegistry registry = {}; - - std::shared_mutex registry_mutex = {}; - SlotMap models = {}; - - std::shared_mutex textures_mutex = {}; - SlotMap textures = {}; - - std::shared_mutex materials_mutex = {}; - SlotMap materials = {}; - std::vector dirty_materials = {}; - - SlotMap, SceneID> scenes = {}; -}; - -auto AssetManager::create(Device *device) -> AssetManager { +auto AssetManager::init(this AssetManager &) -> bool { ZoneScoped; - auto impl = new Impl; - auto self = AssetManager(impl); - - impl->device = device; - impl->root_path = fs::current_path(); - - return self; + return true; } -auto AssetManager::destroy() -> void { +auto AssetManager::destroy(this AssetManager &self) -> void { ZoneScoped; - auto read_lock = std::shared_lock(impl->registry_mutex); + auto read_lock = std::shared_lock(self.registry_mutex); - for (const auto &[asset_uuid, asset] : impl->registry) { + for (const auto &[asset_uuid, asset] : self.registry) { // sanity check if (asset.is_loaded() && asset.ref_count != 0) { - LOG_ERROR( + LOG_TRACE( "A {} asset ({}, {}) with refcount of {} is still alive!", - this->to_asset_type_sv(asset.type), + self.to_asset_type_sv(asset.type), asset_uuid.str(), asset.path, asset.ref_count ); } } - - delete impl; } -auto AssetManager::asset_root_path(AssetType type) -> fs::path { +auto AssetManager::asset_root_path(this AssetManager &self, AssetType type) -> fs::path { ZoneScoped; - auto root = impl->root_path / "resources"; + auto root = self.root_path / "resources"; switch (type) { case AssetType::Root: return root; @@ -191,7 +164,7 @@ auto AssetManager::asset_root_path(AssetType type) -> fs::path { LS_UNREACHABLE(); } -auto AssetManager::to_asset_file_type(const fs::path &path) -> AssetFileType { +auto AssetManager::to_asset_file_type(this AssetManager &, const fs::path &path) -> AssetFileType { ZoneScoped; memory::ScopedStack stack; @@ -221,7 +194,7 @@ auto AssetManager::to_asset_file_type(const fs::path &path) -> AssetFileType { } } -auto AssetManager::to_asset_type_sv(AssetType type) -> std::string_view { +auto AssetManager::to_asset_type_sv(this AssetManager &, AssetType type) -> std::string_view { ZoneScoped; switch (type) { @@ -247,15 +220,15 @@ auto AssetManager::to_asset_type_sv(AssetType type) -> std::string_view { LS_UNREACHABLE(); } -auto AssetManager::registry() const -> const AssetRegistry & { - return impl->registry; +auto AssetManager::get_registry(this AssetManager &self) -> const AssetRegistry & { + return self.registry; } -auto AssetManager::create_asset(AssetType type, const fs::path &path) -> UUID { +auto AssetManager::create_asset(this AssetManager &self, AssetType type, const fs::path &path) -> UUID { ZoneScoped; auto uuid = UUID::generate_random(); - auto [asset_it, inserted] = impl->registry.try_emplace(uuid); + auto [asset_it, inserted] = self.registry.try_emplace(uuid); if (!inserted) { LOG_ERROR("Cannot create assert '{}'!", uuid.str()); return UUID(nullptr); @@ -269,12 +242,12 @@ auto AssetManager::create_asset(AssetType type, const fs::path &path) -> UUID { return asset.uuid; } -auto AssetManager::init_new_scene(const UUID &uuid, const std::string &name) -> bool { +auto AssetManager::init_new_scene(this AssetManager &self, const UUID &uuid, const std::string &name) -> bool { ZoneScoped; - auto *asset = this->get_asset(uuid); - asset->scene_id = impl->scenes.create_slot(std::make_unique()); - auto *scene = impl->scenes.slot(asset->scene_id)->get(); + auto *asset = self.get_asset(uuid); + asset->scene_id = self.scenes.create_slot(std::make_unique()); + auto *scene = self.scenes.slot(asset->scene_id)->get(); if (!scene->init(name)) { return false; } @@ -285,7 +258,7 @@ auto AssetManager::init_new_scene(const UUID &uuid, const std::string &name) -> return true; } -auto AssetManager::import_asset(const fs::path &path) -> UUID { +auto AssetManager::import_asset(this AssetManager &self, const fs::path &path) -> UUID { ZoneScoped; memory::ScopedStack stack; @@ -295,9 +268,9 @@ auto AssetManager::import_asset(const fs::path &path) -> UUID { } auto asset_type = AssetType::None; - switch (this->to_asset_file_type(path)) { + switch (self.to_asset_file_type(path)) { case AssetFileType::Meta: { - return this->register_asset(path); + return self.register_asset(path); } case AssetFileType::GLB: case AssetFileType::GLTF: { @@ -318,10 +291,10 @@ auto AssetManager::import_asset(const fs::path &path) -> UUID { // Check for meta file before creating new asset auto meta_path = stack.format("{}.lrasset", path); if (fs::exists(meta_path)) { - return this->register_asset(meta_path); + return self.register_asset(meta_path); } - auto uuid = this->create_asset(asset_type, path); + auto uuid = self.create_asset(asset_type, path); if (!uuid) { return UUID(nullptr); } @@ -340,11 +313,11 @@ auto AssetManager::import_asset(const fs::path &path) -> UUID { std::visit( ls::match{ [&](const std::vector &) { // - texture_uuid = this->create_asset(AssetType::Texture, path); + texture_uuid = self.create_asset(AssetType::Texture, path); embedded_textures.push_back(texture_uuid); }, [&](const fs::path &image_path) { // - texture_uuid = this->import_asset(image_path); + texture_uuid = self.import_asset(image_path); }, }, image.image_data @@ -354,7 +327,7 @@ auto AssetManager::import_asset(const fs::path &path) -> UUID { auto material_uuids = std::vector(gltf_model->materials.size()); auto materials = std::vector(gltf_model->materials.size()); for (const auto &[material_uuid, material, gltf_material] : std::views::zip(material_uuids, materials, gltf_model->materials)) { - material_uuid = this->create_asset(AssetType::Material); + material_uuid = self.create_asset(AssetType::Material); material.albedo_color = gltf_material.albedo_color; material.emissive_color = gltf_material.emissive_color; material.roughness_factor = gltf_material.roughness_factor; @@ -400,6 +373,15 @@ auto AssetManager::import_asset(const fs::path &path) -> UUID { return uuid; } +auto AssetManager::import_project(this AssetManager &self, const fs::path &path) -> void { + ZoneScoped; + + for (const auto &entry : fs::recursive_directory_iterator(path)) { + const auto &cur_path = entry.path(); + self.import_asset(cur_path); + } +} + struct AssetMetaFile { simdjson::padded_string contents; simdjson::ondemand::parser parser; @@ -432,7 +414,7 @@ auto read_meta_file(const fs::path &path) -> std::unique_ptr { return result; } -auto AssetManager::register_asset(const fs::path &path) -> UUID { +auto AssetManager::register_asset(this AssetManager &self, const fs::path &path) -> UUID { ZoneScoped; memory::ScopedStack stack; @@ -458,20 +440,20 @@ auto AssetManager::register_asset(const fs::path &path) -> UUID { auto uuid = UUID::from_string(uuid_json.value_unsafe()).value(); auto type = static_cast(type_json.value_unsafe().get_uint64()); - if (!this->register_asset(uuid, type, asset_path)) { + if (!self.register_asset(uuid, type, asset_path)) { return UUID(nullptr); } return uuid; } -auto AssetManager::register_asset(const UUID &uuid, AssetType type, const fs::path &path) -> bool { +auto AssetManager::register_asset(this AssetManager &self, const UUID &uuid, AssetType type, const fs::path &path) -> bool { ZoneScoped; - auto write_lock = std::unique_lock(impl->registry_mutex); - auto [asset_it, inserted] = impl->registry.try_emplace(uuid); + auto write_lock = std::unique_lock(self.registry_mutex); + auto [asset_it, inserted] = self.registry.try_emplace(uuid); if (!inserted) { - if (asset_it != impl->registry.end()) { + if (asset_it != self.registry.end()) { // Tried a reinsert, asset already exists return true; } @@ -487,19 +469,19 @@ auto AssetManager::register_asset(const UUID &uuid, AssetType type, const fs::pa return true; } -auto AssetManager::load_asset(const UUID &uuid) -> bool { +auto AssetManager::load_asset(this AssetManager &self, const UUID &uuid) -> bool { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); switch (asset->type) { case AssetType::Model: { - return this->load_model(uuid); + return self.load_model(uuid); } case AssetType::Texture: { - return this->load_texture(uuid); + return self.load_texture(uuid); } case AssetType::Scene: { - return this->load_scene(uuid); + return self.load_scene(uuid); } default:; } @@ -507,20 +489,20 @@ auto AssetManager::load_asset(const UUID &uuid) -> bool { return false; } -auto AssetManager::unload_asset(const UUID &uuid) -> bool { +auto AssetManager::unload_asset(this AssetManager &self, const UUID &uuid) -> bool { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); LS_EXPECT(asset); switch (asset->type) { case AssetType::Model: { - return this->unload_model(uuid); + return self.unload_model(uuid); } break; case AssetType::Texture: { - return this->unload_texture(uuid); + return self.unload_texture(uuid); } break; case AssetType::Scene: { - return this->unload_scene(uuid); + return self.unload_scene(uuid); } break; default:; } @@ -528,11 +510,11 @@ auto AssetManager::unload_asset(const UUID &uuid) -> bool { return false; } -auto AssetManager::load_model(const UUID &uuid) -> bool { +auto AssetManager::load_model(this AssetManager &self, const UUID &uuid) -> bool { ZoneScoped; memory::ScopedStack stack; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); if (asset->is_loaded()) { // Model is collection of multiple assets and all child // assets must be alive to safely process meshes. @@ -542,8 +524,8 @@ auto AssetManager::load_model(const UUID &uuid) -> bool { return true; } - asset->model_id = impl->models.create_slot(); - auto *model = impl->models.slot(asset->model_id); + asset->model_id = self.models.create_slot(); + auto *model = self.models.slot(asset->model_id); fs::path meta_path = asset->path.string() + ".lrasset"; auto meta_json = read_meta_file(meta_path); @@ -576,7 +558,7 @@ auto AssetManager::load_model(const UUID &uuid) -> bool { } embedded_textures.push_back(embedded_texture_uuid.value()); - this->register_asset(embedded_texture_uuid.value(), AssetType::Texture, asset_path); + self.register_asset(embedded_texture_uuid.value(), AssetType::Texture, asset_path); } // Load registered UUIDs. @@ -600,7 +582,7 @@ auto AssetManager::load_model(const UUID &uuid) -> bool { return false; } - this->register_asset(material_uuid.value(), AssetType::Material, asset_path); + self.register_asset(material_uuid.value(), AssetType::Material, asset_path); model->materials.emplace_back(material_uuid.value()); } @@ -647,10 +629,11 @@ auto AssetManager::load_model(const UUID &uuid) -> bool { } for (const auto &[material_uuid, material_info] : std::views::zip(model->materials, embedded_material_infos)) { - this->load_material(material_uuid, material_info); + self.load_material(material_uuid, material_info); } struct GLTFCallbacks { + AssetManager *asset_man = nullptr; Model *model = nullptr; std::vector vertex_positions = {}; @@ -660,7 +643,6 @@ auto AssetManager::load_model(const UUID &uuid) -> bool { }; auto on_new_primitive = [](void *user_data, u32 mesh_index, u32 material_index, u32 vertex_offset, u32 vertex_count, u32 index_offset, u32 index_count) { - auto &app = Application::get(); auto *info = static_cast(user_data); if (info->model->meshes.size() <= mesh_index) { info->model->meshes.resize(mesh_index + 1); @@ -669,7 +651,7 @@ auto AssetManager::load_model(const UUID &uuid) -> bool { auto &mesh = info->model->meshes[mesh_index]; auto primitive_index = info->model->primitives.size(); auto &primitive = info->model->primitives.emplace_back(); - auto *material_asset = app.asset_man.get_asset(info->model->materials[material_index]); + auto *material_asset = info->asset_man->get_asset(info->model->materials[material_index]); info->model->gpu_meshes.emplace_back(); info->model->gpu_mesh_buffers.emplace_back(); @@ -703,7 +685,7 @@ auto AssetManager::load_model(const UUID &uuid) -> bool { info->vertex_texcoords[offset] = texcoord; }; - GLTFCallbacks gltf_callbacks = { .model = model }; + GLTFCallbacks gltf_callbacks = { .asset_man = &self, .model = model }; auto gltf_model = GLTFModelInfo::parse( asset_path, { .user_data = &gltf_callbacks, @@ -718,7 +700,8 @@ auto AssetManager::load_model(const UUID &uuid) -> bool { return false; } - auto &transfer_man = impl->device->transfer_man(); + auto &device = App::mod(); + auto &transfer_man = device.transfer_man(); // ── SCENE HIERARCHY ───────────────────────────────────────────────── for (const auto &node : gltf_model->nodes) { @@ -972,7 +955,7 @@ auto AssetManager::load_model(const UUID &uuid) -> bool { } auto mesh_upload_offset = 0_u64; - gpu_mesh_buffer = Buffer::create(*impl->device, upload_size, vuk::MemoryUsage::eGPUonly).value(); + gpu_mesh_buffer = Buffer::create(device, upload_size, vuk::MemoryUsage::eGPUonly).value(); // Mesh first auto cpu_mesh_buffer = transfer_man.alloc_transient_buffer(vuk::MemoryUsage::eCPUonly, mesh_upload_size); @@ -993,7 +976,7 @@ auto AssetManager::load_model(const UUID &uuid) -> bool { mesh_upload_offset += ls::size_bytes(mesh_texcoords); } - auto gpu_mesh_buffer_handle = impl->device->buffer(gpu_mesh_buffer.id()); + auto gpu_mesh_buffer_handle = device.buffer(gpu_mesh_buffer.id()); auto gpu_mesh_subrange = vuk::discard_buf("mesh", gpu_mesh_buffer_handle->subrange(0, mesh_upload_size)); gpu_mesh_subrange = transfer_man.upload_staging(std::move(cpu_mesh_buffer), std::move(gpu_mesh_subrange)); transfer_man.wait_on(std::move(gpu_mesh_subrange)); @@ -1020,39 +1003,40 @@ auto AssetManager::load_model(const UUID &uuid) -> bool { return true; } -auto AssetManager::unload_model(const UUID &uuid) -> bool { +auto AssetManager::unload_model(this AssetManager &self, const UUID &uuid) -> bool { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); LS_EXPECT(asset); if (!(asset->is_loaded() && asset->release_ref())) { return false; } - auto *model = this->get_model(asset->model_id); + auto *model = self.get_model(asset->model_id); for (auto &v : model->materials) { - this->unload_material(v); + self.unload_material(v); } + auto &device = App::mod(); for (const auto &buffer : model->gpu_mesh_buffers) { - impl->device->destroy(buffer.id()); + device.destroy(buffer.id()); } - impl->models.destroy_slot(asset->model_id); + self.models.destroy_slot(asset->model_id); asset->model_id = ModelID::Invalid; return true; } -auto AssetManager::load_texture(const UUID &uuid, const TextureInfo &info) -> bool { +auto AssetManager::load_texture(this AssetManager &self, const UUID &uuid, const TextureInfo &info) -> bool { ZoneScoped; memory::ScopedStack stack; auto asset_path = fs::path{}; { - auto read_lock = std::shared_lock(impl->textures_mutex); - auto *asset = this->get_asset(uuid); + auto read_lock = std::shared_lock(self.textures_mutex); + auto *asset = self.get_asset(uuid); LS_EXPECT(asset); asset->acquire_ref(); if (asset->is_loaded()) { @@ -1076,7 +1060,7 @@ auto AssetManager::load_texture(const UUID &uuid, const TextureInfo &info) -> bo return false; } - file_type = this->to_asset_file_type(asset_path); + file_type = self.to_asset_file_type(asset_path); } auto format = vuk::Format::eUndefined; @@ -1108,6 +1092,9 @@ auto AssetManager::load_texture(const UUID &uuid, const TextureInfo &info) -> bo } } + auto &device = App::mod(); + auto &transfer_man = device.transfer_man(); + auto sampler_info = SamplerInfo{ .min_filter = vuk::Filter::eLinear, .mag_filter = vuk::Filter::eLinear, @@ -1122,9 +1109,9 @@ auto AssetManager::load_texture(const UUID &uuid, const TextureInfo &info) -> bo .max_lod = static_cast(mip_level_count - 1), .use_anisotropy = true, }; - auto sampler = Sampler::create(*impl->device, sampler_info).value(); + auto sampler = Sampler::create(device, sampler_info).value(); - auto rel_path = fs::relative(asset_path, impl->root_path); + auto rel_path = fs::relative(asset_path, self.root_path); auto image_info = ImageInfo{ .format = format, .usage = vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eTransferSrc, @@ -1134,7 +1121,7 @@ auto AssetManager::load_texture(const UUID &uuid, const TextureInfo &info) -> bo .mip_count = mip_level_count, .name = stack.format("{} Image", rel_path), }; - auto image = Image::create(*impl->device, image_info).value(); + auto image = Image::create(device, image_info).value(); auto subresource_range = vuk::ImageSubresourceRange{ .aspectMask = vuk::ImageAspectFlagBits::eColor, @@ -1149,10 +1136,9 @@ auto AssetManager::load_texture(const UUID &uuid, const TextureInfo &info) -> bo .subresource_range = subresource_range, .name = stack.format("{} Image View", rel_path), }; - auto image_view = ImageView::create(*impl->device, image, image_view_info).value(); - auto dst_attachment = image_view.discard(*impl->device, "dst image", vuk::ImageUsageFlagBits::eTransferDst); + auto image_view = ImageView::create(device, image, image_view_info).value(); + auto dst_attachment = image_view.discard(device, "dst image", vuk::ImageUsageFlagBits::eTransferDst); - auto &transfer_man = impl->device->transfer_man(); switch (file_type) { case AssetFileType::PNG: case AssetFileType::JPEG: { @@ -1207,9 +1193,9 @@ auto AssetManager::load_texture(const UUID &uuid, const TextureInfo &info) -> bo } { - auto write_lock = std::unique_lock(impl->textures_mutex); - auto *asset = this->get_asset(uuid); - asset->texture_id = impl->textures.create_slot(Texture{ .image = image, .image_view = image_view, .sampler = sampler }); + auto write_lock = std::unique_lock(self.textures_mutex); + auto *asset = self.get_asset(uuid); + asset->texture_id = self.textures.create_slot(Texture{ .image = image, .image_view = image_view, .sampler = sampler }); } LOG_TRACE("Loaded texture {}.", uuid.str()); @@ -1217,32 +1203,33 @@ auto AssetManager::load_texture(const UUID &uuid, const TextureInfo &info) -> bo return true; } -auto AssetManager::unload_texture(const UUID &uuid) -> bool { +auto AssetManager::unload_texture(this AssetManager &self, const UUID &uuid) -> bool { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); if (!asset || (!(asset->is_loaded() && asset->release_ref()))) { return false; } - auto *texture = this->get_texture(asset->texture_id); - impl->device->destroy(texture->image_view.id()); - impl->device->destroy(texture->image.id()); - impl->device->destroy(texture->sampler.id()); + auto &device = App::mod(); + auto *texture = self.get_texture(asset->texture_id); + device.destroy(texture->image_view.id()); + device.destroy(texture->image.id()); + device.destroy(texture->sampler.id()); LOG_TRACE("Unloaded texture {}.", uuid.str()); - impl->textures.destroy_slot(asset->texture_id); + self.textures.destroy_slot(asset->texture_id); asset->texture_id = TextureID::Invalid; return true; } -auto AssetManager::is_texture_loaded(const UUID &uuid) -> bool { +auto AssetManager::is_texture_loaded(this AssetManager &self, const UUID &uuid) -> bool { ZoneScoped; - auto read_lock = std::shared_lock(impl->textures_mutex); - auto *asset = this->get_asset(uuid); + auto read_lock = std::shared_lock(self.textures_mutex); + auto *asset = self.get_asset(uuid); if (!asset) { return false; } @@ -1250,185 +1237,184 @@ auto AssetManager::is_texture_loaded(const UUID &uuid) -> bool { return asset->is_loaded(); } -auto AssetManager::load_material(const UUID &uuid, const MaterialInfo &info) -> bool { +auto AssetManager::load_material(this AssetManager &self, const UUID &uuid, const MaterialInfo &info) -> bool { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); LS_EXPECT(asset); if (asset->is_loaded()) { asset->acquire_ref(); return true; } - auto &app = Application::get(); - asset->material_id = impl->materials.create_slot(const_cast(info.material)); - auto *material = impl->materials.slot(asset->material_id); + asset->material_id = self.materials.create_slot(const_cast(info.material)); + auto *material = self.materials.slot(asset->material_id); #if 1 if (material->albedo_texture) { - auto job = Job::create([this, // + auto job = Job::create([&self, // texture_uuid = material->albedo_texture, texture_info = info.albedo_texture_info, material_id = asset->material_id]() { - this->load_texture(texture_uuid, texture_info); - this->set_material_dirty(material_id); + self.load_texture(texture_uuid, texture_info); + self.set_material_dirty(material_id); }); - app.job_man->submit(std::move(job)); + App::submit_job(std::move(job)); } if (material->normal_texture) { - auto job = Job::create([this, // + auto job = Job::create([&self, // texture_uuid = material->normal_texture, texture_info = info.normal_texture_info, material_id = asset->material_id]() { - this->load_texture(texture_uuid, texture_info); - this->set_material_dirty(material_id); + self.load_texture(texture_uuid, texture_info); + self.set_material_dirty(material_id); }); - app.job_man->submit(std::move(job)); + App::submit_job(std::move(job)); } if (material->emissive_texture) { - auto job = Job::create([this, // + auto job = Job::create([&self, // texture_uuid = material->emissive_texture, texture_info = info.emissive_texture_info, material_id = asset->material_id]() { - this->load_texture(texture_uuid, texture_info); - this->set_material_dirty(material_id); + self.load_texture(texture_uuid, texture_info); + self.set_material_dirty(material_id); }); - app.job_man->submit(std::move(job)); + App::submit_job(std::move(job)); } if (material->metallic_roughness_texture) { - auto job = Job::create([this, // + auto job = Job::create([&self, // texture_uuid = material->metallic_roughness_texture, texture_info = info.metallic_roughness_texture_info, material_id = asset->material_id]() { - this->load_texture(texture_uuid, texture_info); - this->set_material_dirty(material_id); + self.load_texture(texture_uuid, texture_info); + self.set_material_dirty(material_id); }); - app.job_man->submit(std::move(job)); + App::submit_job(std::move(job)); } if (material->occlusion_texture) { - auto job = Job::create([this, // + auto job = Job::create([&self, // texture_uuid = material->occlusion_texture, texture_info = info.occlusion_texture_info, material_id = asset->material_id]() { - this->load_texture(texture_uuid, texture_info); - this->set_material_dirty(material_id); + self.load_texture(texture_uuid, texture_info); + self.set_material_dirty(material_id); }); - app.job_man->submit(std::move(job)); + App::submit_job(std::move(job)); } #else if (material->albedo_texture) { - this->load_texture(material->albedo_texture, info.albedo_texture_info); + self.load_texture(material->albedo_texture, info.albedo_texture_info); } if (material->normal_texture) { - this->load_texture(material->normal_texture, info.normal_texture_info); + self.load_texture(material->normal_texture, info.normal_texture_info); } if (material->emissive_texture) { - this->load_texture(material->emissive_texture, info.emissive_texture_info); + self.load_texture(material->emissive_texture, info.emissive_texture_info); } if (material->metallic_roughness_texture) { - this->load_texture(material->metallic_roughness_texture, info.metallic_roughness_texture_info); + self.load_texture(material->metallic_roughness_texture, info.metallic_roughness_texture_info); } if (material->occlusion_texture) { - this->load_texture(material->occlusion_texture, info.occlusion_texture_info); + self.load_texture(material->occlusion_texture, info.occlusion_texture_info); } #endif - this->set_material_dirty(asset->material_id); + self.set_material_dirty(asset->material_id); asset->acquire_ref(); return true; } -auto AssetManager::unload_material(const UUID &uuid) -> bool { +auto AssetManager::unload_material(this AssetManager &self, const UUID &uuid) -> bool { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); LS_EXPECT(asset); if (!(asset->is_loaded() && asset->release_ref())) { return false; } - auto *material = this->get_material(asset->material_id); + auto *material = self.get_material(asset->material_id); if (material->albedo_texture) { - this->unload_texture(material->albedo_texture); + self.unload_texture(material->albedo_texture); } if (material->normal_texture) { - this->unload_texture(material->normal_texture); + self.unload_texture(material->normal_texture); } if (material->emissive_texture) { - this->unload_texture(material->emissive_texture); + self.unload_texture(material->emissive_texture); } if (material->metallic_roughness_texture) { - this->unload_texture(material->metallic_roughness_texture); + self.unload_texture(material->metallic_roughness_texture); } if (material->occlusion_texture) { - this->unload_texture(material->occlusion_texture); + self.unload_texture(material->occlusion_texture); } - impl->materials.destroy_slot(asset->material_id); + self.materials.destroy_slot(asset->material_id); asset->material_id = MaterialID::Invalid; return true; } -auto AssetManager::is_material_loaded(const UUID &uuid) -> bool { +auto AssetManager::is_material_loaded(this AssetManager &self, const UUID &uuid) -> bool { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); // Parent asset is not loaded, skip if (!asset || !asset->is_loaded()) { return false; } - auto *material = this->get_material(asset->material_id); - if (material->albedo_texture && this->is_texture_loaded(material->albedo_texture)) { + auto *material = self.get_material(asset->material_id); + if (material->albedo_texture && self.is_texture_loaded(material->albedo_texture)) { return false; } - if (material->normal_texture && this->is_texture_loaded(material->normal_texture)) { + if (material->normal_texture && self.is_texture_loaded(material->normal_texture)) { return false; } - if (material->emissive_texture && this->is_texture_loaded(material->emissive_texture)) { + if (material->emissive_texture && self.is_texture_loaded(material->emissive_texture)) { return false; } - if (material->metallic_roughness_texture && this->is_texture_loaded(material->metallic_roughness_texture)) { + if (material->metallic_roughness_texture && self.is_texture_loaded(material->metallic_roughness_texture)) { return false; } - if (material->occlusion_texture && this->is_texture_loaded(material->occlusion_texture)) { + if (material->occlusion_texture && self.is_texture_loaded(material->occlusion_texture)) { return false; } return true; } -auto AssetManager::load_scene(const UUID &uuid) -> bool { +auto AssetManager::load_scene(this AssetManager &self, const UUID &uuid) -> bool { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); LS_EXPECT(asset); asset->acquire_ref(); if (asset->is_loaded()) { return true; } - asset->scene_id = impl->scenes.create_slot(std::make_unique()); - auto *scene = impl->scenes.slot(asset->scene_id)->get(); + asset->scene_id = self.scenes.create_slot(std::make_unique()); + auto *scene = self.scenes.slot(asset->scene_id)->get(); if (!scene->init("unnamed_scene")) { return false; @@ -1441,45 +1427,45 @@ auto AssetManager::load_scene(const UUID &uuid) -> bool { return true; } -auto AssetManager::unload_scene(const UUID &uuid) -> bool { +auto AssetManager::unload_scene(this AssetManager &self, const UUID &uuid) -> bool { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); LS_EXPECT(asset); if (!(asset->is_loaded() && asset->release_ref())) { return false; } - auto *scene = this->get_scene(asset->scene_id); + auto *scene = self.get_scene(asset->scene_id); scene->destroy(); - impl->scenes.destroy_slot(asset->scene_id); + self.scenes.destroy_slot(asset->scene_id); asset->scene_id = SceneID::Invalid; return true; } -auto AssetManager::export_asset(const UUID &uuid, const fs::path &path) -> bool { +auto AssetManager::export_asset(this AssetManager &self, const UUID &uuid, const fs::path &path) -> bool { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); JsonWriter json = {}; begin_asset_meta(json, uuid, asset->type); switch (asset->type) { case AssetType::Texture: { - if (!this->export_texture(asset->uuid, json, path)) { + if (!self.export_texture(asset->uuid, json, path)) { return false; } } break; case AssetType::Model: { - if (!this->export_model(asset->uuid, json, path)) { + if (!self.export_model(asset->uuid, json, path)) { return false; } } break; case AssetType::Scene: { - if (!this->export_scene(asset->uuid, json, path)) { + if (!self.export_scene(asset->uuid, json, path)) { return false; } } break; @@ -1490,75 +1476,75 @@ auto AssetManager::export_asset(const UUID &uuid, const fs::path &path) -> bool return end_asset_meta(json, path); } -auto AssetManager::export_texture(const UUID &uuid, JsonWriter &json, const fs::path &) -> bool { +auto AssetManager::export_texture(this AssetManager &self, const UUID &uuid, JsonWriter &json, const fs::path &) -> bool { ZoneScoped; - auto *texture = this->get_texture(uuid); + auto *texture = self.get_texture(uuid); LS_EXPECT(texture); return write_texture_asset_meta(json, texture); } -auto AssetManager::export_model(const UUID &uuid, JsonWriter &json, const fs::path &) -> bool { +auto AssetManager::export_model(this AssetManager &self, const UUID &uuid, JsonWriter &json, const fs::path &) -> bool { ZoneScoped; - auto *model = this->get_model(uuid); + auto *model = self.get_model(uuid); LS_EXPECT(model); auto materials = std::vector(model->materials.size()); for (const auto &[material_uuid, material] : std::views::zip(model->materials, materials)) { - material = *this->get_material(material_uuid); + material = *self.get_material(material_uuid); } return write_model_asset_meta(json, model->embedded_textures, model->materials, materials); } -auto AssetManager::export_scene(const UUID &uuid, JsonWriter &json, const fs::path &path) -> bool { +auto AssetManager::export_scene(this AssetManager &self, const UUID &uuid, JsonWriter &json, const fs::path &path) -> bool { ZoneScoped; - auto *scene = this->get_scene(uuid); + auto *scene = self.get_scene(uuid); LS_EXPECT(scene); write_scene_asset_meta(json, scene); return scene->export_to_file(path); } -auto AssetManager::delete_asset(const UUID &uuid) -> void { +auto AssetManager::delete_asset(this AssetManager &self, const UUID &uuid) -> void { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); if (asset->ref_count > 0) { LOG_WARN("Deleting alive asset {} with {} references!", asset->uuid.str(), asset->ref_count); } if (asset->is_loaded()) { asset->ref_count = ls::min(asset->ref_count, 1_u64); - this->unload_asset(uuid); + self.unload_asset(uuid); { - auto write_lock = std::unique_lock(impl->registry_mutex); - impl->registry.erase(uuid); + auto write_lock = std::unique_lock(self.registry_mutex); + self.registry.erase(uuid); } } // LOG_TRACE("Deleted asset {}.", uuid.str()); } -auto AssetManager::get_asset(const UUID &uuid) -> Asset * { +auto AssetManager::get_asset(this AssetManager &self, const UUID &uuid) -> Asset * { ZoneScoped; - auto read_lock = std::shared_lock(impl->registry_mutex); - auto it = impl->registry.find(uuid); - if (it == impl->registry.end()) { + auto read_lock = std::shared_lock(self.registry_mutex); + auto it = self.registry.find(uuid); + if (it == self.registry.end()) { return nullptr; } return &it->second; } -auto AssetManager::get_model(const UUID &uuid) -> Model * { +auto AssetManager::get_model(this AssetManager &self, const UUID &uuid) -> Model * { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); if (asset == nullptr) { return nullptr; } @@ -1568,23 +1554,23 @@ auto AssetManager::get_model(const UUID &uuid) -> Model * { return nullptr; } - return impl->models.slot(asset->model_id); + return self.models.slot(asset->model_id); } -auto AssetManager::get_model(ModelID model_id) -> Model * { +auto AssetManager::get_model(this AssetManager &self, ModelID model_id) -> Model * { ZoneScoped; if (model_id == ModelID::Invalid) { return nullptr; } - return impl->models.slot(model_id); + return self.models.slot(model_id); } -auto AssetManager::get_texture(const UUID &uuid) -> Texture * { +auto AssetManager::get_texture(this AssetManager &self, const UUID &uuid) -> Texture * { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); if (asset == nullptr) { return nullptr; } @@ -1594,23 +1580,23 @@ auto AssetManager::get_texture(const UUID &uuid) -> Texture * { return nullptr; } - return impl->textures.slot(asset->texture_id); + return self.textures.slot(asset->texture_id); } -auto AssetManager::get_texture(TextureID texture_id) -> Texture * { +auto AssetManager::get_texture(this AssetManager &self, TextureID texture_id) -> Texture * { ZoneScoped; if (texture_id == TextureID::Invalid) { return nullptr; } - return impl->textures.slot(texture_id); + return self.textures.slot(texture_id); } -auto AssetManager::get_material(const UUID &uuid) -> Material * { +auto AssetManager::get_material(this AssetManager &self, const UUID &uuid) -> Material * { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); if (asset == nullptr) { return nullptr; } @@ -1620,23 +1606,23 @@ auto AssetManager::get_material(const UUID &uuid) -> Material * { return nullptr; } - return impl->materials.slot(asset->material_id); + return self.materials.slot(asset->material_id); } -auto AssetManager::get_material(MaterialID material_id) -> Material * { +auto AssetManager::get_material(this AssetManager &self, MaterialID material_id) -> Material * { ZoneScoped; if (material_id == MaterialID::Invalid) { return nullptr; } - return impl->materials.slot(material_id); + return self.materials.slot(material_id); } -auto AssetManager::get_scene(const UUID &uuid) -> Scene * { +auto AssetManager::get_scene(this AssetManager &self, const UUID &uuid) -> Scene * { ZoneScoped; - auto *asset = this->get_asset(uuid); + auto *asset = self.get_asset(uuid); if (asset == nullptr) { return nullptr; } @@ -1646,41 +1632,41 @@ auto AssetManager::get_scene(const UUID &uuid) -> Scene * { return nullptr; } - return impl->scenes.slot(asset->scene_id)->get(); + return self.scenes.slot(asset->scene_id)->get(); } -auto AssetManager::get_scene(SceneID scene_id) -> Scene * { +auto AssetManager::get_scene(this AssetManager &self, SceneID scene_id) -> Scene * { ZoneScoped; if (scene_id == SceneID::Invalid) { return nullptr; } - return impl->scenes.slot(scene_id)->get(); + return self.scenes.slot(scene_id)->get(); } -auto AssetManager::set_material_dirty(MaterialID material_id) -> void { +auto AssetManager::set_material_dirty(this AssetManager &self, MaterialID material_id) -> void { ZoneScoped; - auto read_lock = std::shared_lock(impl->materials_mutex); - if (std::ranges::find(impl->dirty_materials, material_id) != impl->dirty_materials.end()) { + auto read_lock = std::shared_lock(self.materials_mutex); + if (std::ranges::find(self.dirty_materials, material_id) != self.dirty_materials.end()) { return; } read_lock.unlock(); - auto write_lock = std::unique_lock(impl->materials_mutex); - impl->dirty_materials.emplace_back(material_id); + auto write_lock = std::unique_lock(self.materials_mutex); + self.dirty_materials.emplace_back(material_id); } -auto AssetManager::get_dirty_material_ids() -> std::vector { +auto AssetManager::get_dirty_material_ids(this AssetManager &self) -> std::vector { ZoneScoped; - auto read_lock = std::shared_lock(impl->materials_mutex); - auto dirty_materials = std::vector(impl->dirty_materials); + auto read_lock = std::shared_lock(self.materials_mutex); + auto dirty_materials = std::vector(self.dirty_materials); read_lock.unlock(); - auto write_lock = std::unique_lock(impl->materials_mutex); - impl->dirty_materials.clear(); + auto write_lock = std::unique_lock(self.materials_mutex); + self.dirty_materials.clear(); return dirty_materials; } diff --git a/Lorr/Engine/Asset/Asset.hh b/Lorr/Engine/Asset/Asset.hh index 9f5abd1c..1de4059f 100755 --- a/Lorr/Engine/Asset/Asset.hh +++ b/Lorr/Engine/Asset/Asset.hh @@ -4,8 +4,6 @@ #include "Engine/Asset/Model.hh" #include "Engine/Asset/UUID.hh" -#include "Engine/Core/Handle.hh" - #include "Engine/Util/JsonWriter.hh" #include "Engine/Scene/Scene.hh" @@ -38,34 +36,48 @@ struct Asset { } }; -// -// ── ASSET FILE ────────────────────────────────────────────────────── -// - using AssetRegistry = ankerl::unordered_dense::map; -struct AssetManager : Handle { - static auto create(Device *device) -> AssetManager; - auto destroy() -> void; +struct AssetManager { + constexpr static auto MODULE_NAME = "Asset Manager"; + + fs::path root_path = fs::current_path(); + AssetRegistry registry = {}; + + std::shared_mutex registry_mutex = {}; + SlotMap models = {}; + + std::shared_mutex textures_mutex = {}; + SlotMap textures = {}; + + std::shared_mutex materials_mutex = {}; + SlotMap materials = {}; + std::vector dirty_materials = {}; + + SlotMap, SceneID> scenes = {}; + + auto init(this AssetManager &) -> bool; + auto destroy(this AssetManager &) -> void; - auto asset_root_path(AssetType type) -> fs::path; - auto to_asset_file_type(const fs::path &path) -> AssetFileType; - auto to_asset_type_sv(AssetType type) -> std::string_view; - auto registry() const -> const AssetRegistry &; + auto asset_root_path(this AssetManager &, AssetType type) -> fs::path; + auto to_asset_file_type(this AssetManager &, const fs::path &path) -> AssetFileType; + auto to_asset_type_sv(this AssetManager &, AssetType type) -> std::string_view; + auto get_registry(this AssetManager &) -> const AssetRegistry &; // ── Created Assets ────────────────────────────────────────────────── // Assets that will be created and asinged new UUID. New `.lrasset` file // will be written in the same directory as target asset. // All created assets will be automatically registered into the registry. // - auto create_asset(AssetType type, const fs::path &path = {}) -> UUID; - auto init_new_scene(const UUID &uuid, const std::string &name) -> bool; + auto create_asset(this AssetManager &, AssetType type, const fs::path &path = {}) -> UUID; + auto init_new_scene(this AssetManager &, const UUID &uuid, const std::string &name) -> bool; // ── Imported Assets ───────────────────────────────────────────────── // If a valid meta file exists in the same path as importing asset, this // function will act like `register_asset`. Otherwise this function will // act like `create_asset` which creates new unique handle to asset with // meta file in the same path. - auto import_asset(const fs::path &path) -> UUID; + auto import_asset(this AssetManager &, const fs::path &path) -> UUID; + auto import_project(this AssetManager &, const fs::path &path) -> void; // ── Registered Assets ─────────────────────────────────────────────── // Assets that already exist in project root and have meta file with @@ -73,52 +85,52 @@ struct AssetManager : Handle { // // Add already existing asset into the registry. // File must end with `.lrasset` extension. - auto register_asset(const fs::path &path) -> UUID; - auto register_asset(const UUID &uuid, AssetType type, const fs::path &path) -> bool; + auto register_asset(this AssetManager &, const fs::path &path) -> UUID; + auto register_asset(this AssetManager &, const UUID &uuid, AssetType type, const fs::path &path) -> bool; // ── Load Assets ───────────────────────────────────────────────────── // Load contents of registered assets. // - auto load_asset(const UUID &uuid) -> bool; - auto unload_asset(const UUID &uuid) -> bool; + auto load_asset(this AssetManager &, const UUID &uuid) -> bool; + auto unload_asset(this AssetManager &, const UUID &uuid) -> bool; - auto load_model(const UUID &uuid) -> bool; - auto unload_model(const UUID &uuid) -> bool; + auto load_model(this AssetManager &, const UUID &uuid) -> bool; + auto unload_model(this AssetManager &, const UUID &uuid) -> bool; - auto load_texture(const UUID &uuid, const TextureInfo &info = {}) -> bool; - auto unload_texture(const UUID &uuid) -> bool; - auto is_texture_loaded(const UUID &uuid) -> bool; + auto load_texture(this AssetManager &, const UUID &uuid, const TextureInfo &info = {}) -> bool; + auto unload_texture(this AssetManager &, const UUID &uuid) -> bool; + auto is_texture_loaded(this AssetManager &, const UUID &uuid) -> bool; - auto load_material(const UUID &uuid, const MaterialInfo &info) -> bool; - auto unload_material(const UUID &uuid) -> bool; - auto is_material_loaded(const UUID &uuid) -> bool; + auto load_material(this AssetManager &, const UUID &uuid, const MaterialInfo &info) -> bool; + auto unload_material(this AssetManager &, const UUID &uuid) -> bool; + auto is_material_loaded(this AssetManager &, const UUID &uuid) -> bool; - auto load_scene(const UUID &uuid) -> bool; - auto unload_scene(const UUID &uuid) -> bool; + auto load_scene(this AssetManager &, const UUID &uuid) -> bool; + auto unload_scene(this AssetManager &, const UUID &uuid) -> bool; // ── Exporting Assets ──────────────────────────────────────────────── // All export_# functions must have path, developer have freedom to // export their assets into custom directory if needed, otherwise // default to asset->path. // - auto export_asset(const UUID &uuid, const fs::path &path) -> bool; - auto export_texture(const UUID &uuid, JsonWriter &json, const fs::path &path) -> bool; - auto export_model(const UUID &uuid, JsonWriter &json, const fs::path &path) -> bool; - auto export_scene(const UUID &uuid, JsonWriter &json, const fs::path &path) -> bool; - - auto delete_asset(const UUID &uuid) -> void; - - auto get_asset(const UUID &uuid) -> Asset *; - auto get_model(const UUID &uuid) -> Model *; - auto get_model(ModelID model_id) -> Model *; - auto get_texture(const UUID &uuid) -> Texture *; - auto get_texture(TextureID texture_id) -> Texture *; - auto get_material(const UUID &uuid) -> Material *; - auto get_material(MaterialID material_id) -> Material *; - auto get_scene(const UUID &uuid) -> Scene *; - auto get_scene(SceneID scene_id) -> Scene *; - - auto set_material_dirty(MaterialID material_id) -> void; - auto get_dirty_material_ids() -> std::vector; + auto export_asset(this AssetManager &, const UUID &uuid, const fs::path &path) -> bool; + auto export_texture(this AssetManager &, const UUID &uuid, JsonWriter &json, const fs::path &path) -> bool; + auto export_model(this AssetManager &, const UUID &uuid, JsonWriter &json, const fs::path &path) -> bool; + auto export_scene(this AssetManager &, const UUID &uuid, JsonWriter &json, const fs::path &path) -> bool; + + auto delete_asset(this AssetManager &, const UUID &uuid) -> void; + + auto get_asset(this AssetManager &, const UUID &uuid) -> Asset *; + auto get_model(this AssetManager &, const UUID &uuid) -> Model *; + auto get_model(this AssetManager &, ModelID model_id) -> Model *; + auto get_texture(this AssetManager &, const UUID &uuid) -> Texture *; + auto get_texture(this AssetManager &, TextureID texture_id) -> Texture *; + auto get_material(this AssetManager &, const UUID &uuid) -> Material *; + auto get_material(this AssetManager &, MaterialID material_id) -> Material *; + auto get_scene(this AssetManager &, const UUID &uuid) -> Scene *; + auto get_scene(this AssetManager &, SceneID scene_id) -> Scene *; + + auto set_material_dirty(this AssetManager &, MaterialID material_id) -> void; + auto get_dirty_material_ids(this AssetManager &) -> std::vector; }; } // namespace lr diff --git a/Lorr/Engine/Core/App.cc b/Lorr/Engine/Core/App.cc new file mode 100755 index 00000000..486ea96a --- /dev/null +++ b/Lorr/Engine/Core/App.cc @@ -0,0 +1,86 @@ +#include "Engine/Core/App.hh" + +#include "Engine/OS/Timer.hh" + +namespace lr { +static ls::option APP = ls::nullopt; + +auto log_cb( + [[maybe_unused]] i64 ns, + [[maybe_unused]] fmtlog::LogLevel level, + [[maybe_unused]] fmt::string_view location, + [[maybe_unused]] usize basePos, + [[maybe_unused]] fmt::string_view threadName, + [[maybe_unused]] fmt::string_view msg, + [[maybe_unused]] usize bodyPos, + [[maybe_unused]] usize logFilePos +) -> void { + ZoneScoped; + + fmt::println("{}", msg); +} + +auto App::get() -> App & { + return APP.value(); +} + +App::App(u32 worker_count_, ModuleRegistry &&modules_) : should_close(false), job_man(worker_count_), modules(std::move(modules_)) { + ZoneScoped; +} + +void App::run(this App &self) { + ZoneScoped; + + self.job_man.wait(); + self.modules.init(); + self.job_man.wait(); + + Timer timer; + while (!self.should_close) { + auto delta_time = timer.elapsed(); + timer.reset(); + + self.modules.update(delta_time); + + fmtlog::poll(); + + FrameMark; + } + + self.shutdown(); +} + +void App::shutdown(this App &self) { + ZoneScoped; + + LOG_WARN("Shutting down application..."); + + self.job_man.wait(); + self.should_close = true; + self.modules.destroy(); + + LOG_INFO("Complete!"); + + fmtlog::poll(true); +} + +AppBuilder::AppBuilder() { + fmtlog::setThreadName("Main"); + fmtlog::setLogCB(log_cb, fmtlog::DBG); + fmtlog::setHeaderPattern("[{HMSF}] [{t:<9}] {l}: "); + fmtlog::setLogLevel(fmtlog::DBG); + fmtlog::flushOn(fmtlog::WRN); +} + +auto AppBuilder::build(this AppBuilder &self, u32 worker_count, const c8 *log_file_name) -> bool { + LS_EXPECT(!APP.has_value()); + + fmtlog::setLogFile(log_file_name, true); + + APP.emplace(worker_count, std::move(self.registry)); + APP->run(); + + return true; +} + +} // namespace lr diff --git a/Lorr/Engine/Core/App.hh b/Lorr/Engine/Core/App.hh new file mode 100755 index 00000000..a010fcc7 --- /dev/null +++ b/Lorr/Engine/Core/App.hh @@ -0,0 +1,56 @@ +#pragma once + +#include "Engine/Core/JobManager.hh" +#include "Engine/Core/Module.hh" + +namespace lr { +struct AppBuilder; +struct App { + bool should_close; + JobManager job_man; + ModuleRegistry modules; + +public: + static auto get() -> App &; + + template + static auto mod() -> T & { + return get().modules.get(); + } + + static auto close() -> void { + get().should_close = true; + } + + static auto submit_job(Arc job, bool prioritize = false) -> void { + get().job_man.submit(std::move(job), prioritize); + } + +public: + App(u32 worker_count_, ModuleRegistry &&modules_); + void run(this App &); + void shutdown(this App &); + + friend AppBuilder; +}; + +struct AppBuilder { +private: + ModuleRegistry registry = {}; + +public: + AppBuilder(); + + template + auto module(Args &&...args) -> AppBuilder & { + ZoneScoped; + + registry.add(std::forward(args)...); + + return *this; + } + + auto build(this AppBuilder &, u32 worker_count, const c8 *log_file_name) -> bool; +}; + +} // namespace lr diff --git a/Lorr/Engine/Core/Application.cc b/Lorr/Engine/Core/Application.cc deleted file mode 100755 index d5047b06..00000000 --- a/Lorr/Engine/Core/Application.cc +++ /dev/null @@ -1,148 +0,0 @@ -#include "Engine/Core/Application.hh" - -#include "Engine/OS/Timer.hh" - -namespace lr { -auto log_cb( - [[maybe_unused]] i64 ns, - [[maybe_unused]] fmtlog::LogLevel level, - [[maybe_unused]] fmt::string_view location, - [[maybe_unused]] usize basePos, - [[maybe_unused]] fmt::string_view threadName, - [[maybe_unused]] fmt::string_view msg, - [[maybe_unused]] usize bodyPos, - [[maybe_unused]] usize logFilePos -) -> void { - ZoneScoped; - - fmt::println("{}", msg); -} - -bool Application::init(this Application &self, const ApplicationInfo &info) { - ZoneScoped; - - fmtlog::setThreadName("Main"); - fmtlog::setLogFile("engine.log", true); - fmtlog::setLogCB(log_cb, fmtlog::DBG); - fmtlog::setHeaderPattern("[{HMSF}] [{t:<9}] {l}: "); - fmtlog::setLogLevel(fmtlog::DBG); - fmtlog::flushOn(fmtlog::WRN); - - if (!self.do_super_init(info.args)) { - LOG_FATAL("Super init failed!"); - return false; - } - - self.job_man.emplace(8); - self.device.init(3).value(); - self.asset_man = AssetManager::create(&self.device); - self.window = Window::create(info.window_info); - auto surface = self.window.get_surface(self.device.get_instance()); - self.swap_chain.emplace(self.device.create_swap_chain(surface).value()); - self.imgui_renderer.init(&self.device); - self.scene_renderer.init(&self.device); - - if (!self.do_prepare()) { - LOG_FATAL("Failed to initialize application!"); - return false; - } - - self.job_man->wait(); - - self.run(); - - return true; -} - -void Application::run(this Application &self) { - ZoneScoped; - - WindowCallbacks window_callbacks = {}; - window_callbacks.user_data = &self; - window_callbacks.on_resize = [](void *user_data, glm::uvec2) { - auto *app = static_cast(user_data); - app->device.wait(); - - auto surface = app->window.get_surface(app->device.get_instance()); - app->swap_chain = app->device.create_swap_chain(surface, std::move(app->swap_chain)).value(); - }; - window_callbacks.on_close = [](void *user_data) { - auto *app = static_cast(user_data); - app->should_close = true; - }; - window_callbacks.on_mouse_pos = [](void *user_data, glm::vec2 position, glm::vec2) { - auto *app = static_cast(user_data); - app->imgui_renderer.on_mouse_pos(position); - }; - window_callbacks.on_mouse_button = [](void *user_data, u8 button, bool down) { - auto *app = static_cast(user_data); - app->imgui_renderer.on_mouse_button(button, down); - }; - window_callbacks.on_mouse_scroll = [](void *user_data, glm::vec2 offset) { - auto *app = static_cast(user_data); - app->imgui_renderer.on_mouse_scroll(offset); - }; - window_callbacks.on_key = [](void *user_data, SDL_Keycode key_code, SDL_Scancode scan_code, u16 mods, bool down) { - auto *app = static_cast(user_data); - app->imgui_renderer.on_key(key_code, scan_code, mods, down); - }; - window_callbacks.on_text_input = [](void *user_data, const c8 *text) { - auto *app = static_cast(user_data); - app->imgui_renderer.on_text_input(text); - }; - - Timer timer; - while (!self.should_close) { - auto delta_time = timer.elapsed(); - timer.reset(); - - self.window.poll(window_callbacks); - - auto swapchain_attachment = self.device.new_frame(self.swap_chain.value()); - swapchain_attachment = vuk::clear_image(std::move(swapchain_attachment), vuk::Black); - - self.imgui_renderer.begin_frame(delta_time, swapchain_attachment->extent); - self.do_update(delta_time); - self.do_render(swapchain_attachment->format, swapchain_attachment->extent); - - swapchain_attachment = self.imgui_renderer.end_frame(std::move(swapchain_attachment)); - self.device.end_frame(std::move(swapchain_attachment)); - - fmtlog::poll(); - - FrameMark; - } - - self.shutdown(); -} - -void Application::shutdown(this Application &self) { - ZoneScoped; - - LOG_WARN("Shutting down application..."); - - self.job_man->wait(); - self.device.wait(); - - self.should_close = true; - - self.do_shutdown(); - - self.job_man->shutdown(); - self.job_man->wait(); - - self.imgui_renderer.destroy(); - self.scene_renderer.destroy(); - self.swap_chain.reset(); - - self.asset_man.destroy(); - fmtlog::poll(true); - - self.device.destroy(); - - LOG_INFO("Complete!"); - - fmtlog::poll(true); -} - -} // namespace lr diff --git a/Lorr/Engine/Core/Application.hh b/Lorr/Engine/Core/Application.hh deleted file mode 100755 index ad303f4b..00000000 --- a/Lorr/Engine/Core/Application.hh +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "Engine/Asset/Asset.hh" - -#include "Engine/Core/JobManager.hh" - -#include "Engine/Graphics/ImGuiRenderer.hh" -#include "Engine/Graphics/Vulkan.hh" -#include "Engine/Graphics/VulkanDevice.hh" - -#include "Engine/Window/Window.hh" - -#include "Engine/Scene/SceneRenderer.hh" - -namespace lr { -struct ApplicationInfo { - ls::span args = {}; - WindowInfo window_info = {}; -}; - -struct Application { - // Prevent `Application` from being copied, `::get()` only has to return a reference - Application() = default; - Application(const Application &) = delete; - Application(Application &&) = delete; - Application &operator=(const Application &) = delete; - Application &operator=(Application &&) = delete; - - static Application &get(); - - ls::option job_man = ls::nullopt; - Device device = {}; - Window window = {}; - ls::option swap_chain = ls::nullopt; - ImGuiRenderer imgui_renderer = {}; - SceneRenderer scene_renderer = {}; - AssetManager asset_man = {}; - - bool should_close = false; - - bool init(this Application &, const ApplicationInfo &info); - void run(this Application &); - void shutdown(this Application &); - - virtual bool do_super_init(ls::span args) = 0; - virtual bool do_prepare() = 0; - virtual bool do_update(f64 delta_time) = 0; - virtual bool do_render(vuk::Format format, vuk::Extent3D extent) = 0; - virtual void do_shutdown() = 0; -}; - -} // namespace lr diff --git a/Lorr/Engine/Core/CoreModule.hh b/Lorr/Engine/Core/CoreModule.hh deleted file mode 100644 index e249fa5c..00000000 --- a/Lorr/Engine/Core/CoreModule.hh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -namespace lr { -struct ICoreModule {}; -} diff --git a/Lorr/Engine/Core/Module.cc b/Lorr/Engine/Core/Module.cc new file mode 100644 index 00000000..e8ab284d --- /dev/null +++ b/Lorr/Engine/Core/Module.cc @@ -0,0 +1,38 @@ +#include "Engine/Core/Module.hh" + +namespace lr { +auto ModuleRegistry::init(this ModuleRegistry &self) -> bool { + ZoneScoped; + + for (const auto &[name, cb] : std::views::zip(self.module_names, self.init_callbacks)) { + auto start_ts = std::chrono::high_resolution_clock::now(); + if (!cb()) { + LOG_ERROR("Module {} failed to initialize!", name); + return false; + } + + auto end_ts = std::chrono::high_resolution_clock::now(); + auto delta = std::chrono::duration(end_ts - start_ts); + LOG_INFO("Initialized module {} in {} ms.", name, delta.count()); + } + + return true; +} + +auto ModuleRegistry::update(this ModuleRegistry &self, f64 delta_time) -> void { + ZoneScoped; + + for (const auto &cb : self.update_callbacks) { + if (LS_LIKELY(cb.has_value())) { + cb.value()(delta_time); + } + } +} + +auto ModuleRegistry::destroy(this ModuleRegistry &self) -> void { + for (const auto &cb : std::views::reverse(self.destroy_callbacks)) { + cb(); + } +} + +} // namespace lr diff --git a/Lorr/Engine/Core/Module.hh b/Lorr/Engine/Core/Module.hh new file mode 100644 index 00000000..f12e597b --- /dev/null +++ b/Lorr/Engine/Core/Module.hh @@ -0,0 +1,74 @@ +#pragma once + +#include + +namespace lr { +template +concept Module = requires(T t) { + t.init(); + t.destroy(); + { + T::MODULE_NAME + } -> std::convertible_to; +}; + +template +concept ModuleHasUpdate = requires(T t, f64 delta_time) { t.update(delta_time); }; + +struct TypeIndexHash { + std::size_t operator()(const std::type_index &ti) const noexcept { + return ti.hash_code(); + } +}; + +struct ModuleRegistry { + using ModulePtr = std::unique_ptr; + using Registry = ankerl::unordered_dense::map; + + Registry registry = {}; + std::vector> init_callbacks = {}; + std::vector>> update_callbacks = {}; + std::vector> destroy_callbacks = {}; + std::vector module_names = {}; + + template + auto add(Args &&...args) -> void { + ZoneScoped; + + auto type_index = std::type_index(typeid(T)); + auto deleter = [](void *self) { delete static_cast(self); }; + auto &module = registry.try_emplace(type_index, ModulePtr(new T(std::forward(args)...), deleter)).first->second; + + init_callbacks.push_back([module = static_cast(module.get())]() { return module->init(); }); + destroy_callbacks.push_back([module = static_cast(module.get())]() { module->destroy(); }); + if constexpr (ModuleHasUpdate) { + update_callbacks.push_back([module = static_cast(module.get())](f64 delta_time) { module->update(delta_time); }); + } else { + update_callbacks.emplace_back(ls::nullopt); + } + module_names.push_back(T::MODULE_NAME); + } + + template + auto has() -> bool { + ZoneScoped; + + return registry.contains(std::type_index(typeid(T))); + } + + template + auto get() -> T & { + ZoneScoped; + + auto it = registry.find(std::type_index(typeid(T))); + LS_EXPECT(it != registry.end()); + + return *static_cast(it->second.get()); + } + + auto init(this ModuleRegistry &) -> bool; + auto update(this ModuleRegistry &, f64 delta_time) -> void; + auto destroy(this ModuleRegistry &) -> void; +}; + +} // namespace lr diff --git a/Lorr/Engine/Graphics/ImGuiRenderer.cc b/Lorr/Engine/Graphics/ImGuiRenderer.cc index 3b97e55b..e4012116 100644 --- a/Lorr/Engine/Graphics/ImGuiRenderer.cc +++ b/Lorr/Engine/Graphics/ImGuiRenderer.cc @@ -1,6 +1,8 @@ #include "Engine/Graphics/ImGuiRenderer.hh" -#include "Engine/Core/Application.hh" +#include "Engine/Asset/Asset.hh" + +#include "Engine/Core/App.hh" #include "Engine/Graphics/VulkanDevice.hh" @@ -9,16 +11,16 @@ #include #include "Engine/Util/Icons/IconsMaterialDesignIcons.hh" +#include "Engine/Window/Window.hh" namespace lr { -auto ImGuiRenderer::init(this ImGuiRenderer &self, Device *device) -> void { +auto ImGuiRenderer::init(this ImGuiRenderer &self) -> bool { ZoneScoped; - self.device = device; - - auto &app = Application::get(); - auto shaders_root = app.asset_man.asset_root_path(AssetType::Shader); - auto fonts_root = app.asset_man.asset_root_path(AssetType::Font); + auto &asset_man = App::mod(); + auto &device = App::mod(); + auto shaders_root = asset_man.asset_root_path(AssetType::Shader); + auto fonts_root = asset_man.asset_root_path(AssetType::Font); auto roboto_path = (fonts_root / "Roboto-Regular.ttf").string(); auto materialdesignicons_path = (fonts_root / FONT_ICON_FILE_NAME_MDI).string(); @@ -57,21 +59,30 @@ auto ImGuiRenderer::init(this ImGuiRenderer &self, Device *device) -> void { imgui.Fonts->TexDesiredFormat = ImTextureFormat_RGBA32; imgui.FontDefault = roboto_font; - auto slang_session = device->new_slang_session({ .root_directory = shaders_root }).value(); + auto slang_session = device.new_slang_session({ .root_directory = shaders_root }).value(); auto pipeline_info = PipelineCompileInfo{ .module_name = "passes.imgui", .entry_points = { "vs_main", "fs_main" }, }; - self.pipeline = Pipeline::create(*device, slang_session, pipeline_info).value(); + self.pipeline = Pipeline::create(device, slang_session, pipeline_info).value(); + + auto &window = App::mod(); + window.add_listener(self); + + return true; } auto ImGuiRenderer::destroy(this ImGuiRenderer &self) -> void { + ZoneScoped; + + auto &device = App::mod(); + if (self.font_image_view) { - self.device->destroy(self.font_image_view.id()); + device.destroy(self.font_image_view.id()); } if (self.font_image) { - self.device->destroy(self.font_image.id()); + device.destroy(self.font_image.id()); } } @@ -85,13 +96,14 @@ auto ImGuiRenderer::add_image(this ImGuiRenderer &self, vuk::Value ImTextureID { ZoneScoped; + auto &device = App::mod(); + auto acquired_it = self.acquired_images.find(image_view.id()); if (acquired_it != self.acquired_images.end()) { return acquired_it->second; } - auto attachment = - image_view.acquire(*self.device, "imgui rendering image", vuk::ImageUsageFlagBits::eSampled, vuk::Access::eFragmentSampled, LOC); + auto attachment = image_view.acquire(device, "imgui rendering image", vuk::ImageUsageFlagBits::eSampled, vuk::Access::eFragmentSampled, LOC); auto texture_id = self.add_image(std::move(attachment)); self.acquired_images.emplace(image_view.id(), texture_id); @@ -101,7 +113,6 @@ auto ImGuiRenderer::add_image(this ImGuiRenderer &self, ImageView &image_view, L auto ImGuiRenderer::begin_frame(this ImGuiRenderer &self, f64 delta_time, const vuk::Extent3D &extent) -> void { ZoneScoped; - auto &app = Application::get(); auto &imgui = ImGui::GetIO(); imgui.DeltaTime = static_cast(delta_time); imgui.DisplaySize = ImVec2(static_cast(extent.width), static_cast(extent.height)); @@ -116,31 +127,31 @@ auto ImGuiRenderer::begin_frame(this ImGuiRenderer &self, f64 delta_time, const return; } - auto imgui_cursor = ImGui::GetMouseCursor(); - if (imgui.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) { - app.window.show_cursor(false); - } else { - auto next_cursor = WindowCursor::Arrow; - // clang-format off - switch (imgui_cursor) { - case ImGuiMouseCursor_Arrow: next_cursor = WindowCursor::Arrow; break; - case ImGuiMouseCursor_TextInput: next_cursor = WindowCursor::TextInput; break; - case ImGuiMouseCursor_ResizeAll: next_cursor = WindowCursor::ResizeAll; break; - case ImGuiMouseCursor_ResizeNS: next_cursor = WindowCursor::ResizeNS; break; - case ImGuiMouseCursor_ResizeEW: next_cursor = WindowCursor::ResizeEW; break; - case ImGuiMouseCursor_ResizeNESW: next_cursor = WindowCursor::ResizeNESW; break; - case ImGuiMouseCursor_ResizeNWSE: next_cursor = WindowCursor::ResizeNWSE; break; - case ImGuiMouseCursor_Hand: next_cursor = WindowCursor::Hand; break; - case ImGuiMouseCursor_NotAllowed: next_cursor = WindowCursor::NotAllowed; break; - default: break; - } - // clang-format on - app.window.show_cursor(true); - - if (app.window.get_cursor() != next_cursor) { - app.window.set_cursor(next_cursor); - } - } + // auto imgui_cursor = ImGui::GetMouseCursor(); + // if (imgui.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) { + // app.window.show_cursor(false); + // } else { + // auto next_cursor = WindowCursor::Arrow; + // // clang-format off + // switch (imgui_cursor) { + // case ImGuiMouseCursor_Arrow: next_cursor = WindowCursor::Arrow; break; + // case ImGuiMouseCursor_TextInput: next_cursor = WindowCursor::TextInput; break; + // case ImGuiMouseCursor_ResizeAll: next_cursor = WindowCursor::ResizeAll; break; + // case ImGuiMouseCursor_ResizeNS: next_cursor = WindowCursor::ResizeNS; break; + // case ImGuiMouseCursor_ResizeEW: next_cursor = WindowCursor::ResizeEW; break; + // case ImGuiMouseCursor_ResizeNESW: next_cursor = WindowCursor::ResizeNESW; break; + // case ImGuiMouseCursor_ResizeNWSE: next_cursor = WindowCursor::ResizeNWSE; break; + // case ImGuiMouseCursor_Hand: next_cursor = WindowCursor::Hand; break; + // case ImGuiMouseCursor_NotAllowed: next_cursor = WindowCursor::NotAllowed; break; + // default: break; + // } + // // clang-format on + // app.window.show_cursor(true); + // + // if (app.window.get_cursor() != next_cursor) { + // app.window.set_cursor(next_cursor); + // } + // } } auto ImGuiRenderer::end_frame(this ImGuiRenderer &self, vuk::Value &&attachment) -> vuk::Value { @@ -148,7 +159,8 @@ auto ImGuiRenderer::end_frame(this ImGuiRenderer &self, vuk::Valuetransfer_man(); + auto &device = App::mod(); + auto &transfer_man = device.transfer_man(); auto *draw_data = ImGui::GetDrawData(); if (draw_data->Textures) { @@ -167,13 +179,10 @@ auto ImGuiRenderer::end_frame(this ImGuiRenderer &self, vuk::ValueWidth, texture->Height, 1u), .name = "ImGui Font", }; - std::tie(self.font_image, self.font_image_view) = Image::create_with_view(*self.device, image_info).value(); - acquired_image = self.font_image_view.acquire( - *self.device, - "imgui image", - vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eTransferDst, - vuk::eNone - ); + std::tie(self.font_image, self.font_image_view) = Image::create_with_view(device, image_info).value(); + acquired_image = + self.font_image_view + .acquire(device, "imgui image", vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eTransferDst, vuk::eNone); acquired = true; upload_offset = {}; @@ -182,17 +191,14 @@ auto ImGuiRenderer::end_frame(this ImGuiRenderer &self, vuk::Valuenon_coherent_atom_size(); + auto buffer_alignment = device.non_coherent_atom_size(); auto upload_pitch = upload_extent.width * texture->BytesPerPixel; auto buffer_size = ls::align_up(upload_pitch * upload_extent.height, buffer_alignment); auto upload_buffer = transfer_man.alloc_transient_buffer(vuk::MemoryUsage::eCPUonly, buffer_size); auto *buffer_ptr = reinterpret_cast(upload_buffer->mapped_ptr); for (auto y = 0_u32; y < upload_extent.height; y++) { - std::memcpy( - buffer_ptr + upload_pitch * y, - texture->GetPixelsAt(upload_offset.x, upload_offset.y + static_cast(y)), - upload_pitch - ); + auto *pixels = static_cast(texture->GetPixelsAt(upload_offset.x, upload_offset.y + static_cast(y))); + std::memcpy(buffer_ptr + upload_pitch * y, pixels, upload_pitch); } auto upload_pass = vuk::make_pass( @@ -215,7 +221,7 @@ auto ImGuiRenderer::end_frame(this ImGuiRenderer &self, vuk::ValueSetStatus(ImTextureStatus_OK); } break; case ImTextureStatus_OK: { - acquired_image = - self.font_image_view.acquire(*self.device, "imgui image", vuk::ImageUsageFlagBits::eSampled, vuk::eFragmentSampled); + acquired_image = self.font_image_view.acquire(device, "imgui image", vuk::ImageUsageFlagBits::eSampled, vuk::eFragmentSampled); auto texture_id = self.add_image(std::move(acquired_image)); texture->SetTexID(texture_id); } break; case ImTextureStatus_WantDestroy: { - self.device->destroy(self.font_image.id()); - self.device->destroy(self.font_image_view.id()); + device.destroy(self.font_image.id()); + device.destroy(self.font_image_view.id()); } break; case ImTextureStatus_Destroyed:; } } } + auto imgui_rendering_images_arr = vuk::declare_array("imgui rendering images", std::span(self.rendering_images)); + u64 vertex_size_bytes = draw_data->TotalVtxCount * sizeof(ImDrawVert); u64 index_size_bytes = draw_data->TotalIdxCount * sizeof(ImDrawIdx); if (!draw_data || vertex_size_bytes == 0) { + if (!self.rendering_images.empty()) { + transfer_man.wait_on(std::move(imgui_rendering_images_arr)); + } + return std::move(attachment); } @@ -334,11 +345,36 @@ auto ImGuiRenderer::end_frame(this ImGuiRenderer &self, vuk::Value void { + ZoneScoped; + + switch (e.type) { + case SDL_EVENT_MOUSE_MOTION: { + self.on_mouse_pos({ e.motion.x, e.motion.y }); + } break; + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: { + auto state = e.type == SDL_EVENT_MOUSE_BUTTON_DOWN; + self.on_mouse_button(e.button.button, state); + } break; + case SDL_EVENT_MOUSE_WHEEL: { + self.on_mouse_scroll({ e.wheel.x, e.wheel.y }); + } break; + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: { + auto state = e.type == SDL_EVENT_KEY_DOWN; + self.on_key(e.key.key, e.key.scancode, e.key.mod, state); + } break; + case SDL_EVENT_TEXT_INPUT: { + self.on_text_input(e.text.text); + } break; + default:; + } +} + auto ImGuiRenderer::on_mouse_pos(this ImGuiRenderer &, glm::vec2 pos) -> void { ZoneScoped; diff --git a/Lorr/Engine/Graphics/ImGuiRenderer.hh b/Lorr/Engine/Graphics/ImGuiRenderer.hh index a3aa3860..8cb3be74 100644 --- a/Lorr/Engine/Graphics/ImGuiRenderer.hh +++ b/Lorr/Engine/Graphics/ImGuiRenderer.hh @@ -2,12 +2,14 @@ #include "Engine/Graphics/Vulkan.hh" +#include #include #include namespace lr { struct ImGuiRenderer { - Device *device = nullptr; + static constexpr auto MODULE_NAME = "ImGui Renderer"; + Pipeline pipeline = {}; Image font_image = {}; ImageView font_image_view = {}; @@ -15,7 +17,7 @@ struct ImGuiRenderer { std::vector> rendering_images = {}; ankerl::unordered_dense::map acquired_images = {}; - auto init(this ImGuiRenderer &, Device *device) -> void; + auto init(this ImGuiRenderer &) -> bool; auto destroy(this ImGuiRenderer &) -> void; auto add_font(this ImGuiRenderer &, const fs::path &path) -> ImFont *; @@ -25,6 +27,7 @@ struct ImGuiRenderer { auto begin_frame(this ImGuiRenderer &, f64 delta_time, const vuk::Extent3D &extent) -> void; auto end_frame(this ImGuiRenderer &, vuk::Value &&attachment) -> vuk::Value; + auto window_event(this ImGuiRenderer &, SDL_Event &e) -> void; auto on_mouse_pos(this ImGuiRenderer &, glm::vec2 pos) -> void; auto on_mouse_button(this ImGuiRenderer &, u8 button, bool down) -> void; auto on_mouse_scroll(this ImGuiRenderer &, glm::vec2 offset) -> void; diff --git a/Lorr/Engine/Graphics/Slang/Compiler.cc b/Lorr/Engine/Graphics/Slang/Compiler.cc index 4415552e..c2392ddd 100644 --- a/Lorr/Engine/Graphics/Slang/Compiler.cc +++ b/Lorr/Engine/Graphics/Slang/Compiler.cc @@ -104,8 +104,6 @@ struct SlangVirtualFS : ISlangFileSystem { if (!file_bytes.empty()) { auto new_it = m_loaded_modules.emplace(module_name, std::move(file_bytes)); *outBlob = new SlangBlobSpan(new_it.first->second); - - LOG_TRACE("Loaded new shader module '{}'", module_name); return SLANG_OK; } else { return SLANG_E_NOT_FOUND; diff --git a/Lorr/Engine/Graphics/Vulkan/Device.cc b/Lorr/Engine/Graphics/Vulkan/Device.cc index fe4af8ed..2b4fb0f2 100644 --- a/Lorr/Engine/Graphics/Vulkan/Device.cc +++ b/Lorr/Engine/Graphics/Vulkan/Device.cc @@ -25,19 +25,17 @@ constexpr fmtlog::LogLevel to_log_category(VkDebugUtilsMessageSeverityFlagBitsEX return fmtlog::DBG; } -auto Device::init(this Device &self, usize frame_count) -> std::expected { +auto Device::init(this Device &self) -> bool { ZoneScoped; - self.frames_in_flight = frame_count; - vkb::InstanceBuilder instance_builder; instance_builder.set_app_name("Lorr App"); instance_builder.set_engine_name("Lorr"); instance_builder.set_engine_version(1, 0, 0); instance_builder.enable_validation_layers(false); // use vkconfig ui... instance_builder.request_validation_layers(false); - instance_builder.add_debug_messenger_severity(VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT); - instance_builder.add_debug_messenger_type(VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT); + // instance_builder.add_debug_messenger_severity(VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT); + // instance_builder.add_debug_messenger_type(VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT); instance_builder.set_debug_callback( [](VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -74,7 +72,7 @@ auto Device::init(this Device &self, usize frame_count) -> std::expected std::expected std::expected std::expectedset_shader_target_version(VK_API_VERSION_1_4); self.shader_compiler = SlangCompiler::create().value(); @@ -250,12 +247,12 @@ auto Device::init(this Device &self, usize frame_count) -> std::expected std::expected { @@ -290,7 +287,7 @@ auto Device::init_resources(this Device &self) -> std::expected &&tar }; auto result = vuk::enqueue_presentation(std::move(target_attachment)); - result.submit( - *self.transfer_manager.frame_allocator, - self.compiler, - { .graph_label = {}, - .callbacks = { - .on_begin_pass = on_begin_pass, - .on_end_pass = on_end_pass, - .user_data = &self, - } } - ); + try { + result.submit( + *self.transfer_manager.frame_allocator, + self.compiler, + { .graph_label = {}, + .callbacks = { + .on_begin_pass = on_begin_pass, + .on_end_pass = on_end_pass, + .user_data = &self, + } } + ); + } catch (std::exception &e) { + } } auto Device::wait(this Device &self, LR_CALLSTACK) -> void { diff --git a/Lorr/Engine/Graphics/VulkanDevice.hh b/Lorr/Engine/Graphics/VulkanDevice.hh index 7adbf492..b3ce1c43 100644 --- a/Lorr/Engine/Graphics/VulkanDevice.hh +++ b/Lorr/Engine/Graphics/VulkanDevice.hh @@ -105,6 +105,8 @@ struct DeviceResources { }; struct Device { + constexpr static auto MODULE_NAME = "Vulkan Device"; + private: usize frames_in_flight = 0; ls::option runtime; @@ -132,9 +134,9 @@ private: friend TransferManager; public: - auto init(this Device &, usize frame_count) -> std::expected; + Device(usize frame_count_) : frames_in_flight(frame_count_) {} + auto init(this Device &) -> bool; auto init_resources(this Device &) -> std::expected; - auto destroy(this Device &) -> void; auto new_slang_session(this Device &, const SlangSessionInfo &info) -> ls::option; diff --git a/Lorr/Engine/Resources/shaders/passes/brdf.slang b/Lorr/Engine/Resources/shaders/passes/brdf.slang index f38af678..82ffe8d4 100644 --- a/Lorr/Engine/Resources/shaders/passes/brdf.slang +++ b/Lorr/Engine/Resources/shaders/passes/brdf.slang @@ -19,8 +19,7 @@ struct ShaderParameters { Image2D emissive_image; Image2D metallic_roughness_occlusion_image; - ConstantBuffer atmosphere; - ConstantBuffer sun; + ConstantBuffer environment; ConstantBuffer camera; }; ParameterBlock params; @@ -52,36 +51,45 @@ func fs_main(VertexOutput input) -> f32x4 { // PBR constants const f32x3 V = normalize(params.camera.position - world_position); - const f32x3 L = normalize(params.sun.direction); // temp + const f32x3 L = normalize(params.environment.sun_direction); // temp const f32x3 N = mapped_normal; - // SUN LIGHT COLOR ────────────────────────────────────────────────── - const f32x3 ray_pos_planet = f32x3(0.0, world_position.y, 0.0) * CAMERA_SCALE_UNIT + f32x3(0.0, params.atmosphere.planet_radius, 0.0); - f32 h = length(ray_pos_planet); - f32x3 up_vec = normalize(ray_pos_planet); - f32 sun_cos_theta = dot(L, up_vec); - f32x2 transmittance_uv = transmittance_params_to_lut_uv(params.atmosphere, f32x2(h, sun_cos_theta)); - f32x3 sun_transmittance = params.sky_transmittance_lut.sample_mip(params.linear_clamp_sampler, transmittance_uv, 0.0).rgb; - f32x3 sun_illuminance = sun_transmittance * params.sun.intensity; - - // SKY AMBIENT COLOR ──────────────────────────────────────────────── - AtmosphereIntegrateInfo sky_info = {}; - sky_info.eye_pos = ray_pos_planet; - sky_info.sun_dir = L; - sky_info.sun_intensity = params.sun.intensity; - sky_info.sampling.variable_sample_count = true; - sky_info.sampling.min_sample_count = 1; - sky_info.sampling.max_sample_count = 4; - sky_info.transmittance_image = params.sky_transmittance_lut; - sky_info.multiscattering_image = params.sky_multiscattering_lut; - sky_info.eval_mie_phase = false; - - sky_info.eye_dir = f32x3(0.0, 1.0, 0.0); - const let sky_result = integrate_single_scattered_luminance(params.atmosphere, params.linear_clamp_sampler, sky_info); - - f32 eye_gradient = dot(N, sky_info.eye_dir); - eye_gradient = (eye_gradient + 1.0) * 0.375 + 0.25; - f32x3 ambient_contribution = std::rec709_oetf(sky_result.luminance) * albedo_color * occlusion * eye_gradient; + var sun_illuminance = f32x3(1.0); + var sky_luminance = f32x3(1.0); + if (params.environment.flags & (EnvironmentFlags::HasSun | EnvironmentFlags::HasAtmosphere)) { + // SUN LIGHT COLOR ────────────────────────────────────────────────── + var eye_altitude = world_position.y * CAMERA_SCALE_UNIT; + eye_altitude += params.environment.atmos_planet_radius + PLANET_RADIUS_OFFSET; + var eye_pos = f32x3(0.0, eye_altitude, 0.0); + let up_vec = f32x3(0.0, 1.0, 0.0); + f32 sun_cos_theta = dot(L, up_vec); + f32x2 transmittance_uv = transmittance_params_to_lut_uv( + params.environment.atmos_atmos_radius, + params.environment.atmos_planet_radius, + f32x2(eye_altitude, sun_cos_theta)); + f32x3 sun_transmittance = params.sky_transmittance_lut.sample_mip(params.linear_clamp_sampler, transmittance_uv, 0.0).rgb; + sun_illuminance = sun_transmittance * params.environment.sun_intensity; + + // SKY AMBIENT COLOR ──────────────────────────────────────────────── + AtmosphereIntegrateInfo sky_info = {}; + sky_info.eye_pos = eye_pos; + sky_info.eye_dir = up_vec; + sky_info.sun_dir = L; + sky_info.sun_intensity = params.environment.sun_intensity; + sky_info.sampling.variable_sample_count = true; + sky_info.sampling.min_sample_count = 1; + sky_info.sampling.max_sample_count = 4; + sky_info.transmittance_image = params.sky_transmittance_lut; + sky_info.multiscattering_image = params.sky_multiscattering_lut; + sky_info.eval_mie_phase = false; + let sky_result = integrate_single_scattered_luminance(params.environment, params.linear_clamp_sampler, sky_info); + + var eye_gradient = dot(N, up_vec); + eye_gradient = (eye_gradient + 1.0) * 0.375 + 0.25; + sky_luminance = std::rec709_oetf(sky_result.luminance) * eye_gradient; + } + + f32x3 ambient_contribution = sky_luminance * albedo_color * occlusion; // MATERIAL COLOR ─────────────────────────────────────────────────── // https://marmosetco.tumblr.com/post/81245981087 diff --git a/Lorr/Engine/Resources/shaders/passes/cull_meshlets.slang b/Lorr/Engine/Resources/shaders/passes/cull_meshlets.slang index a92cfdfb..d547632f 100644 --- a/Lorr/Engine/Resources/shaders/passes/cull_meshlets.slang +++ b/Lorr/Engine/Resources/shaders/passes/cull_meshlets.slang @@ -70,7 +70,6 @@ func cs_main( if (visible) { let index = std::atomic_add(params.cull_triangles_cmd[0].x, 1, std::memory_order_relaxed); - assert(index < meshlet_instance_count); params.visible_meshlet_instances_indices[index] = meshlet_instance_index; } } diff --git a/Lorr/Engine/Resources/shaders/passes/histogram_average.slang b/Lorr/Engine/Resources/shaders/passes/histogram_average.slang index 04cdb0f6..ecb13043 100644 --- a/Lorr/Engine/Resources/shaders/passes/histogram_average.slang +++ b/Lorr/Engine/Resources/shaders/passes/histogram_average.slang @@ -2,36 +2,37 @@ module histogram_average; import std; import gpu; +import scene; #include -struct PushConstants { - u32 *histogram; - HistogramLuminance *luminance; - f32 pixel_count; - f32 min_exposure; - f32 exposure_range; - f32 time_coeff; - f32 ISO_K; +struct ShaderParameters { + ConstantBuffer environment; + StructuredBuffer histogram_bin_indices; + + RWStructuredBuffer luminance; }; -[[vk::push_constant]] PushConstants C; groupshared f32 histogram_shared[HISTOGRAM_BIN_COUNT]; -func ev100_from_luminance(f32 luminance) -> f32 { - return log2(luminance * C.ISO_K); +func ev100_from_luminance(f32 luminance, f32 ISO_K) -> f32 { + return log2(luminance * ISO_K); } [[shader("compute")]] [[numthreads(HISTOGRAM_BIN_COUNT, 1, 1)]] -func cs_main(u32 gid : SV_GroupIndex) -> void { - const let count_for_this_bin = gid == 0 ? 0.0 : f32(C.histogram[gid]); +func cs_main( + u32 gid : SV_GroupIndex, + uniform ParameterBlock params, + uniform f32 pixel_count, + uniform f32 delta_time +) -> void { + let count_for_this_bin = gid == 0 ? 0.0 : f32(params.histogram_bin_indices[gid]); histogram_shared[gid] = count_for_this_bin * f32(gid); std::control_barrier(std::memory_order_acq_rel); [[unroll]] - for (u32 cutoff = (HISTOGRAM_BIN_COUNT >> 1); cutoff > 0; cutoff >>= 1) - { + for (u32 cutoff = (HISTOGRAM_BIN_COUNT >> 1); cutoff > 0; cutoff >>= 1) { if (gid < cutoff) { histogram_shared[gid] += histogram_shared[gid + cutoff]; } @@ -40,12 +41,16 @@ func cs_main(u32 gid : SV_GroupIndex) -> void { } if (gid == 0) { - const f32 weighted_average_log2 = (histogram_shared[0] / max(C.pixel_count - count_for_this_bin, 1.0)) - 1.0; - const f32 desired_luminance = exp2(((weighted_average_log2 / (HISTOGRAM_BIN_COUNT - 1)) * C.exposure_range) + C.min_exposure); - const f32 last_luminance = C.luminance->adapted_luminance; - const f32 adapted_luminance = last_luminance + (desired_luminance - last_luminance) * C.time_coeff; - const f32 ev100 = ev100_from_luminance(adapted_luminance); - C.luminance->adapted_luminance = adapted_luminance; - C.luminance->exposure = 1.0 / (exp2(ev100) * 1.2); + let exposure_range = params.environment.eye_max_exposure - params.environment.eye_min_exposure; + let time_coeff = clamp(1.0 - exp(-params.environment.eye_adaptation_speed * delta_time), 0.0f, 1.0f); + + let weighted_average_log2 = (histogram_shared[0] / max(pixel_count - count_for_this_bin, 1.0)) - 1.0; + let desired_luminance = + exp2(((weighted_average_log2 / (HISTOGRAM_BIN_COUNT - 1)) * exposure_range) + params.environment.eye_min_exposure); + let last_luminance = params.luminance[0].adapted_luminance; + let adapted_luminance = last_luminance + (desired_luminance - last_luminance) * time_coeff; + let ev100 = ev100_from_luminance(adapted_luminance, params.environment.eye_ISO_K); + let exposure = 1.0 / (exp2(ev100) * 1.2); + params.luminance[0] = HistogramLuminance(adapted_luminance, exposure); } } diff --git a/Lorr/Engine/Resources/shaders/passes/histogram_generate.slang b/Lorr/Engine/Resources/shaders/passes/histogram_generate.slang index 60e6d2cd..e2367161 100644 --- a/Lorr/Engine/Resources/shaders/passes/histogram_generate.slang +++ b/Lorr/Engine/Resources/shaders/passes/histogram_generate.slang @@ -2,44 +2,48 @@ module histogram_generate; import std; import gpu; +import scene; #include -[[vk::binding(0, 0)]] -Image2D src_image; +struct ShaderParameters { + Image2D src_image; -struct PushConstants { - u32 *histogram; - u32x2 src_image_size; - f32 min_exposure; - f32 exposure_range_inv; + ConstantBuffer environment; + + RWStructuredBuffer histogram_bin_indices; }; -[[vk::push_constant]] PushConstants C; groupshared u32 histogram_shared[HISTOGRAM_BIN_COUNT]; -func bin_lum(f32 luminance) -> u32 { +func bin_lum(f32 luminance, f32 max_exposure, f32 min_exposure) -> u32 { if (luminance < LUMINANCE_EPSILON) { return 0; } - const f32 log_luminance = saturate((log2(luminance) - C.min_exposure) * C.exposure_range_inv); + let exposure_range_inv = 1.0 / (max_exposure - min_exposure); + let log_luminance = saturate((log2(luminance) - min_exposure) * exposure_range_inv); return u32(log_luminance * f32(HISTOGRAM_BIN_COUNT - 1) + 1.0); } [[shader("compute")]] [[numthreads(HISTOGRAM_THREADS_X, HISTOGRAM_THREADS_Y, 1)]] -func cs_main(u32 group_index : SV_GroupIndex, u32x3 thread_id : SV_DispatchThreadID) -> void { +func cs_main( + u32 group_index : SV_GroupIndex, + u32x3 thread_id : SV_DispatchThreadID, + uniform ParameterBlock params, + uniform i32x3 src_extent +) -> void { histogram_shared[group_index] = 0; std::control_barrier(std::memory_order_acq_rel); - if (all(thread_id.xy < C.src_image_size)) { - const f32x3 color = src_image.load(thread_id.xy).rgb; + if (all(thread_id.xy < src_extent.xy)) { + const f32x3 color = params.src_image.load(thread_id.xy).rgb; const f32 luminance = std::rec2020_to_xyz(color).y; - const u32 bin_index = bin_lum(luminance); + const u32 bin_index = bin_lum(luminance, params.environment.eye_max_exposure, params.environment.eye_min_exposure); std::atomic_add(histogram_shared[bin_index], 1, std::memory_order_acq_rel); } std::control_barrier(std::memory_order_acq_rel); - std::atomic_add(C.histogram[group_index], histogram_shared[group_index], std::memory_order_relaxed); + std::atomic_add(params.histogram_bin_indices[group_index], histogram_shared[group_index], std::memory_order_relaxed); } diff --git a/Lorr/Engine/Resources/shaders/passes/sky_aerial_perspective.slang b/Lorr/Engine/Resources/shaders/passes/sky_aerial_perspective.slang index 5de0a5f1..e43a1c59 100644 --- a/Lorr/Engine/Resources/shaders/passes/sky_aerial_perspective.slang +++ b/Lorr/Engine/Resources/shaders/passes/sky_aerial_perspective.slang @@ -9,8 +9,7 @@ struct ShaderParameters { Sampler sampler; Image2D sky_transmittance_lut; Image2D sky_multiscattering_lut; - ConstantBuffer atmosphere; - ConstantBuffer sun; + ConstantBuffer environment; ConstantBuffer camera; StorageImage3D sky_aerial_perspective_lut; }; @@ -21,28 +20,31 @@ func cs_main( u32x3 thread_id : SV_DispatchThreadID, uniform ParameterBlock params ) -> void { - const let lut_size = params.atmosphere.aerial_perspective_lut_size; - f32x2 uv = f32x2(f32x2(thread_id.xy) + 0.5) / f32x2(lut_size.xy); - f32x3 NDC = f32x3(2.0 * uv - 1.0, 1.0); - f32x4 world_pos_h = mul(params.camera.inv_projection_view_mat, f32x4(NDC, 1.0)); - f32x3 world_pos = world_pos_h.xyz / world_pos_h.w; - f32x3 world_dir = normalize(world_pos - params.camera.position); - f32x3 eye_pos = params.atmosphere.eye_pos; + let lut_size = params.environment.aerial_perspective_lut_size; + let uv = f32x2(f32x2(thread_id.xy) + 0.5) / f32x2(lut_size.xy); + let NDC = f32x3(2.0 * uv - 1.0, 1.0); + let world_pos_h = mul(params.camera.inv_projection_view_mat, f32x4(NDC, 1.0)); + let world_pos = world_pos_h.xyz / world_pos_h.w; + let world_dir = normalize(world_pos - params.camera.position); + var eye_altitude = params.camera.position.y * CAMERA_SCALE_UNIT; + eye_altitude += params.environment.atmos_planet_radius + PLANET_RADIUS_OFFSET; + var eye_pos = f32x3(0.0, eye_altitude, 0.0); f32 slice = ((f32(thread_id.z) + 0.5) * (1.0 / lut_size.z)); slice *= slice; slice *= lut_size.z; - const i32 step_count = int(max(1.0, f32(thread_id.z + 1.0) * 2.0)); - const f32 per_slice_depth = f32(lut_size.x / lut_size.z); - const f32 start_depth = params.atmosphere.aerial_perspective_start_km * INV_CAMERA_SCALE_UNIT; - f32 t_max = slice * per_slice_depth; + let step_count = int(max(1.0, f32(thread_id.z + 1.0) * 2.0)); + let per_slice_depth = f32(lut_size.x / lut_size.z); + let start_depth = params.environment.atmos_aerial_perspective_start_km * INV_CAMERA_SCALE_UNIT; + var t_max = slice * per_slice_depth; - f32x3 start_pos = eye_pos + start_depth * world_dir; - f32x3 ray_pos = start_pos + t_max * world_dir; - f32 view_height = length(ray_pos); + let start_pos = eye_pos + start_depth * world_dir; + let ray_pos = start_pos + t_max * world_dir; + var view_height = 0.0; /* + view_height = length(ray_pos); if (view_height <= (C.atmosphere.planet_radius + PLANET_RADIUS_OFFSET)) { ray_pos = normalize(ray_pos) * (C.atmosphere.planet_radius + PLANET_RADIUS_OFFSET); world_dir = normalize(ray_pos - eye_pos); @@ -51,10 +53,10 @@ func cs_main( */ f32 t_max_max = t_max; - view_height = length(eye_pos); - if (view_height >= params.atmosphere.atmos_radius) { + view_height = eye_altitude; + if (view_height >= params.environment.atmos_atmos_radius) { f32x3 prev_ray_pos = eye_pos; - if (!move_to_top_atmosphere(eye_pos, world_dir, params.atmosphere.atmos_radius)) { + if (!move_to_top_atmosphere(eye_pos, world_dir, params.environment.atmos_atmos_radius)) { params.sky_aerial_perspective_lut.Store(thread_id, 0.0); return; } @@ -70,8 +72,8 @@ func cs_main( AtmosphereIntegrateInfo info = {}; info.eye_pos = eye_pos; info.eye_dir = world_dir; - info.sun_dir = params.sun.direction; - info.sun_intensity = params.sun.intensity; + info.sun_dir = params.environment.sun_direction; + info.sun_intensity = params.environment.sun_intensity; info.max_integration_length = t_max_max; info.eval_planet_luminance = false; @@ -81,7 +83,7 @@ func cs_main( info.transmittance_image = params.sky_transmittance_lut; info.multiscattering_image = params.sky_multiscattering_lut; - const let result = integrate_single_scattered_luminance(params.atmosphere, params.sampler, info); - const let transmittance = dot(result.transmittance, f32x3(1.0f / 3.0f)); + let result = integrate_single_scattered_luminance(params.environment, params.sampler, info); + let transmittance = dot(result.transmittance, f32x3(1.0f / 3.0f)); params.sky_aerial_perspective_lut.Store(thread_id, f32x4(result.luminance, transmittance)); } diff --git a/Lorr/Engine/Resources/shaders/passes/sky_final.slang b/Lorr/Engine/Resources/shaders/passes/sky_final.slang index 4588f983..0f939813 100644 --- a/Lorr/Engine/Resources/shaders/passes/sky_final.slang +++ b/Lorr/Engine/Resources/shaders/passes/sky_final.slang @@ -11,8 +11,7 @@ struct ShaderParameters { Image3D sky_aerial_perspective_lut; Image2D sky_view_lut; Image2D depth_image; - ConstantBuffer atmosphere; - ConstantBuffer sun; + ConstantBuffer environment; ConstantBuffer camera; }; @@ -58,36 +57,48 @@ func fs_main( if (depth != 0.0) { f32x3 camera_relative_pos = (world_pos - camera_pos) * CAMERA_SCALE_UNIT; return sample_aerial_perspective( - params.atmosphere, params.sky_aerial_perspective_lut, params.sampler, + params.environment.aerial_perspective_lut_size, input.tex_coord, - camera_relative_pos); + camera_relative_pos, + params.environment.atmos_aerial_perspective_start_km); } - f32x3 eye_dir = normalize(world_pos - params.camera.position); - f32x3 eye_pos = params.atmosphere.eye_pos; - f32 h = length(eye_pos); - f32x3 up = f32x3(0.0, 1.0, 0.0); - f32x3 right = normalize(cross(up, eye_dir)); - f32x3 forward = normalize(cross(right, up)); - const f32x3 sun_dir = normalize(params.sun.direction); - f32x2 light_on_plane = normalize(f32x2(dot(sun_dir, forward), dot(sun_dir, right))); + let eye_dir = normalize(world_pos - params.camera.position); + var eye_altitude = params.camera.position.y * CAMERA_SCALE_UNIT; + eye_altitude += params.environment.atmos_planet_radius + PLANET_RADIUS_OFFSET; + let eye_pos = f32x3(0.0, eye_altitude, 0.0); + let up = f32x3(0.0, 1.0, 0.0); + let right = normalize(cross(up, eye_dir)); + let forward = normalize(cross(right, up)); + let sun_dir = normalize(params.environment.sun_direction); + let light_on_plane = normalize(f32x2(dot(sun_dir, forward), dot(sun_dir, right))); f32 view_zenith_cos_angle = dot(eye_dir, up); - const let planet_intersection = std::ray_sphere_intersect_nearest(eye_pos, eye_dir, params.atmosphere.planet_radius); - f32x2 uv = sky_view_params_to_lut_uv(params.atmosphere, planet_intersection.hasValue, h, view_zenith_cos_angle, light_on_plane); + const let planet_intersection = std::ray_sphere_intersect_nearest(eye_pos, eye_dir, params.environment.atmos_planet_radius); + f32x2 uv = sky_view_params_to_lut_uv( + params.environment.atmos_atmos_radius, + params.environment.atmos_planet_radius, + params.environment.sky_view_lut_size.xy, + planet_intersection.hasValue, + eye_altitude, + view_zenith_cos_angle, + light_on_plane); f32x4 result = params.sky_view_lut.sample_mip(params.sampler, uv, 0.0); f32x3 luminance = result.rgb; f32 transmittance = result.a; f32 sun_cos_theta = dot(sun_dir, up); - f32x2 transmittance_uv = transmittance_params_to_lut_uv(params.atmosphere, f32x2(h, sun_cos_theta)); + f32x2 transmittance_uv = transmittance_params_to_lut_uv( + params.environment.atmos_atmos_radius, + params.environment.atmos_planet_radius, + f32x2(eye_altitude, sun_cos_theta)); f32x3 sun_transmittance = params.sky_transmittance_lut.sample_mip(params.sampler, transmittance_uv, 0.0).rgb; if (!planet_intersection.hasValue) { - luminance += draw_sun(eye_dir, params.sun.direction, 1.0) * params.sun.intensity * sun_transmittance; + luminance += draw_sun(eye_dir, params.environment.sun_direction, 1.0) * params.environment.sun_intensity * sun_transmittance; } return f32x4(luminance, 1.0 - transmittance); diff --git a/Lorr/Engine/Resources/shaders/passes/sky_multiscattering.slang b/Lorr/Engine/Resources/shaders/passes/sky_multiscattering.slang index 86a52e93..1fbe32d3 100644 --- a/Lorr/Engine/Resources/shaders/passes/sky_multiscattering.slang +++ b/Lorr/Engine/Resources/shaders/passes/sky_multiscattering.slang @@ -10,7 +10,7 @@ import embedded.hemisphere; struct ShaderParameters { Sampler sampler; Image2D sky_transmittance_lut; - ConstantBuffer atmosphere; + ConstantBuffer environment; StorageImage2D sky_multiscattering_lut; }; @@ -22,10 +22,10 @@ func cs_main( u32x3 thread_id : SV_DispatchThreadID, uniform ParameterBlock params ) -> void { - f32x2 uv = f32x2(f32x2(thread_id.xy) + 0.5) / f32x2(params.atmosphere.multiscattering_lut_size.xy); + f32x2 uv = f32x2(f32x2(thread_id.xy) + 0.5) / f32x2(params.environment.multiscattering_lut_size.xy); - const f32 atmosphere_thickness = params.atmosphere.atmos_radius - params.atmosphere.planet_radius; - const f32 altitude = params.atmosphere.planet_radius + uv.y * atmosphere_thickness + PLANET_RADIUS_OFFSET; + let atmosphere_thickness = params.environment.atmos_atmos_radius - params.environment.atmos_planet_radius; + let altitude = params.environment.atmos_planet_radius + uv.y * atmosphere_thickness + PLANET_RADIUS_OFFSET; f32 sun_cos_theta = uv.x * 2.0 - 1.0; f32x3 sun_dir = f32x3(0.0, sun_cos_theta, std::safe_sqrt(1.0 - sun_cos_theta * sun_cos_theta)); @@ -46,7 +46,7 @@ func cs_main( f32x3 multi_scattering_as_1 = 0.0; for (int i = 0; i < SAMPLE_COUNT; i++) { info.eye_dir = HEMISPHERE_64[i]; - const let result = integrate_single_scattered_luminance(params.atmosphere, params.sampler, info); + const let result = integrate_single_scattered_luminance(params.environment, params.sampler, info); multi_scattering_as_1 += result.multiscattering_as_1; luminance += result.luminance; } diff --git a/Lorr/Engine/Resources/shaders/passes/sky_transmittance.slang b/Lorr/Engine/Resources/shaders/passes/sky_transmittance.slang index c4d46e30..4781835a 100644 --- a/Lorr/Engine/Resources/shaders/passes/sky_transmittance.slang +++ b/Lorr/Engine/Resources/shaders/passes/sky_transmittance.slang @@ -7,7 +7,7 @@ import scene; struct ShaderParameters { StorageImage2D sky_transmittance_lut; - ConstantBuffer atmosphere; + ConstantBuffer environment; }; [[shader("compute")]] @@ -16,13 +16,13 @@ func cs_main( u32x2 thread_id : SV_DispatchThreadID, uniform ParameterBlock params ) -> void { - f32x2 uv = f32x2(f32x2(thread_id.xy) + 0.5) / f32x2(params.atmosphere.transmittance_lut_size.xy); + f32x2 uv = f32x2(f32x2(thread_id.xy) + 0.5) / f32x2(params.environment.transmittance_lut_size.xy); f32 h = std::safe_sqrt( - params.atmosphere.atmos_radius * params.atmosphere.atmos_radius - - params.atmosphere.planet_radius * params.atmosphere.planet_radius); + params.environment.atmos_atmos_radius * params.environment.atmos_atmos_radius + - params.environment.atmos_planet_radius * params.environment.atmos_planet_radius); f32 rho = h * uv.y; - f32 lut_x = sqrt(rho * rho + params.atmosphere.planet_radius * params.atmosphere.planet_radius); - f32 d_min = params.atmosphere.atmos_radius - lut_x; + f32 lut_x = sqrt(rho * rho + params.environment.atmos_planet_radius * params.environment.atmos_planet_radius); + f32 d_min = params.environment.atmos_atmos_radius - lut_x; f32 d_max = rho + h; f32 d = d_min + uv.x * (d_max - d_min); f32 lut_y = d == 0.0 ? 1.0 : (h * h - rho * rho - d * d) / (2.0 * lut_x * d); @@ -32,13 +32,13 @@ func cs_main( f32x3 ray_pos = f32x3(0.0, 0.0, lut_x); const f32 STEP_COUNT = 1000.0; - f32 distance = std::ray_sphere_intersect_nearest(ray_pos, sun_dir, params.atmosphere.atmos_radius).value; + f32 distance = std::ray_sphere_intersect_nearest(ray_pos, sun_dir, params.environment.atmos_atmos_radius).value; f32 distance_per_step = distance / STEP_COUNT; f32x3 optical_depth = 0.0; for (f32 i = 0.0; i < STEP_COUNT; i += 1.0) { ray_pos += sun_dir * distance_per_step; - let ray_altitude = length(ray_pos) - params.atmosphere.planet_radius; - let medium = MediumScattering(params.atmosphere, ray_altitude); + let ray_altitude = length(ray_pos) - params.environment.atmos_planet_radius; + let medium = MediumScattering(params.environment, ray_altitude); optical_depth += medium.extinction_sum * distance_per_step; } diff --git a/Lorr/Engine/Resources/shaders/passes/sky_view.slang b/Lorr/Engine/Resources/shaders/passes/sky_view.slang index 1db224e9..107b6d99 100644 --- a/Lorr/Engine/Resources/shaders/passes/sky_view.slang +++ b/Lorr/Engine/Resources/shaders/passes/sky_view.slang @@ -9,8 +9,7 @@ struct ShaderParameters { Sampler sampler; Image2D sky_transmittance_lut; Image2D sky_multiscattering_lut; - ConstantBuffer atmosphere; - ConstantBuffer sun; + ConstantBuffer environment; ConstantBuffer camera; StorageImage2D sky_view_lut; }; @@ -21,26 +20,30 @@ func cs_main( u32x3 thread_id : SV_DispatchThreadID, uniform ParameterBlock params ) -> void { - f32x2 uv = f32x2(thread_id.xy) / f32x2(params.atmosphere.sky_view_lut_size.xy); - f32x3 eye_pos = params.atmosphere.eye_pos; - - f32 h = length(eye_pos); - const f32x3 eye_dir = uv_to_sky_view_lut_params(params.atmosphere, uv, h); - - if (!move_to_top_atmosphere(eye_pos, eye_dir, params.atmosphere.atmos_radius)) { + let uv = f32x2(thread_id.xy) / f32x2(params.environment.sky_view_lut_size.xy); + var eye_altitude = params.camera.position.y * CAMERA_SCALE_UNIT; + eye_altitude += params.environment.atmos_planet_radius + PLANET_RADIUS_OFFSET; + var eye_pos = f32x3(0.0, eye_altitude, 0.0); + let eye_dir = uv_to_sky_view_lut_params( + params.environment.atmos_planet_radius, + params.environment.sky_view_lut_size.xy, + uv, + eye_altitude); + + if (!move_to_top_atmosphere(eye_pos, eye_dir, params.environment.atmos_atmos_radius)) { params.sky_view_lut.store(thread_id.xy, 0.0); return; } - f32x3 up_vec = eye_pos / h; - f32 sun_zenith_cos_angle = dot(normalize(params.sun.direction), up_vec); - f32x3 sun_dir = normalize(f32x3(std::safe_sqrt(1.0 - sun_zenith_cos_angle * sun_zenith_cos_angle), sun_zenith_cos_angle, 0.0)); + let up_vec = f32x3(0.0, 1.0, 0.0); + let sun_zenith_cos_angle = dot(normalize(params.environment.sun_direction), up_vec); + let sun_dir = normalize(f32x3(std::safe_sqrt(1.0 - sun_zenith_cos_angle * sun_zenith_cos_angle), sun_zenith_cos_angle, 0.0)); AtmosphereIntegrateInfo info = {}; info.eye_pos = eye_pos; info.eye_dir = eye_dir; info.sun_dir = sun_dir; - info.sun_intensity = params.sun.intensity; + info.sun_intensity = params.environment.sun_intensity; let sample_count = 48; info.sampling.variable_sample_count = true; @@ -50,7 +53,7 @@ func cs_main( info.transmittance_image = params.sky_transmittance_lut; info.multiscattering_image = params.sky_multiscattering_lut; - let result = integrate_single_scattered_luminance(params.atmosphere, params.sampler, info); + let result = integrate_single_scattered_luminance(params.environment, params.sampler, info); let transmittance = dot(result.transmittance, 1.0 / 3.0); params.sky_view_lut.store(thread_id.xy, f32x4(result.luminance, transmittance)); diff --git a/Lorr/Engine/Resources/shaders/passes/tonemap.slang b/Lorr/Engine/Resources/shaders/passes/tonemap.slang index cb0730ab..49f6afe7 100644 --- a/Lorr/Engine/Resources/shaders/passes/tonemap.slang +++ b/Lorr/Engine/Resources/shaders/passes/tonemap.slang @@ -2,18 +2,18 @@ module tonemap; import std; import gpu; +import scene; #include -[[vk::binding(0, 0)]] -Sampler sampler; -[[vk::binding(1, 0)]] -Image2D input_image; +struct ShaderParameters { + Sampler sampler; + Image2D input_image; -struct PushConstants { - HistogramLuminance *luminance; + ConstantBuffer environment; + ConstantBuffer histogram_luminance; }; -[[vk::push_constant]] PushConstants C; +uniform ParameterBlock params; struct VertexOutput { f32x4 position : SV_Position; @@ -148,9 +148,11 @@ float3 agx_tonemapping(float3 color) { [[shader("fragment")]] f32x4 fs_main(VertexOutput input) { - f32x3 color = input_image.sample_mip(sampler, input.tex_coord, 0.0).rgb; - const f32 exposure = C.luminance->exposure; - color = color * (exposure + 1.0); + f32x3 color = params.input_image.sample_mip(params.sampler, input.tex_coord, 0.0).rgb; + if (params.environment.flags & EnvironmentFlags::HasEyeAdaptation) { + let exposure = params.histogram_luminance.exposure; + color = color * (exposure + 1.0); + } //color = ACES_Film(color); //color = ACES_Fitted(color); diff --git a/Lorr/Engine/Resources/shaders/scene.slang b/Lorr/Engine/Resources/shaders/scene.slang index 90bdc0a9..9a936be5 100644 --- a/Lorr/Engine/Resources/shaders/scene.slang +++ b/Lorr/Engine/Resources/shaders/scene.slang @@ -3,8 +3,6 @@ module scene; import std; import gpu; -#include - public const static f32 CAMERA_SCALE_UNIT = 0.01; public const static f32 INV_CAMERA_SCALE_UNIT = 1.0 / CAMERA_SCALE_UNIT; public const static f32 PLANET_RADIUS_OFFSET = 0.001; @@ -30,35 +28,43 @@ public enum CullFlags : u32 { Occlusion, }; -public struct Sun { - public f32x3 direction; - public f32 intensity; +[[Flags]] +public enum EnvironmentFlags : u32 { + None = 0, + HasSun = 1 << 0, + HasAtmosphere = 1 << 1, + HasEyeAdaptation = 1 << 2, }; -public struct Atmosphere { - public f32x3 eye_pos; - - public f32x3 rayleigh_scatter; - public f32 rayleigh_density; - - public f32x3 mie_scatter; - public f32 mie_density; - public f32 mie_extinction; - public f32 mie_asymmetry; - - public f32x3 ozone_absorption; - public f32 ozone_height; - public f32 ozone_thickness; - - public f32x3 terrain_albedo; - public f32 planet_radius; - public f32 atmos_radius; - public f32 aerial_perspective_start_km; - - public i32x3 transmittance_lut_size; - public i32x3 sky_view_lut_size; - public i32x3 multiscattering_lut_size; - public i32x3 aerial_perspective_lut_size; +public struct Environment { + public EnvironmentFlags flags = EnvironmentFlags::None; + // Sun + public f32x3 sun_direction = {}; + public f32 sun_intensity = 10.0f; + // Atmosphere + public f32x3 atmos_rayleigh_scatter = { 0.005802f, 0.013558f, 0.033100f }; + public f32 atmos_rayleigh_density = 8.0f; + public f32x3 atmos_mie_scatter = { 0.003996f, 0.003996f, 0.003996f }; + public f32 atmos_mie_density = 1.2f; + public f32 atmos_mie_extinction = 0.004440f; + public f32 atmos_mie_asymmetry = 3.6f; + public f32x3 atmos_ozone_absorption = { 0.000650f, 0.001881f, 0.000085f }; + public f32 atmos_ozone_height = 25.0f; + public f32 atmos_ozone_thickness = 15.0f; + public f32x3 atmos_terrain_albedo = { 0.3f, 0.3f, 0.3f }; + public f32 atmos_planet_radius = 6360.0f; + public f32 atmos_atmos_radius = 6460.0f; + public f32 atmos_aerial_perspective_start_km = 8.0f; + // Eye adaptation + public f32 eye_min_exposure = -6.0f; + public f32 eye_max_exposure = 18.0f; + public f32 eye_adaptation_speed = 1.1f; + public f32 eye_ISO_K = 100.0f / 12.5f; + + public i32x3 transmittance_lut_size = {}; + public i32x3 sky_view_lut_size = {}; + public i32x3 multiscattering_lut_size = {}; + public i32x3 aerial_perspective_lut_size = {}; }; public struct Camera { @@ -199,10 +205,7 @@ public struct Meshlet { // Takes a local triange index and returns an index to index buffer. public func index(in MeshLOD mesh_lod, u32 i) -> u32 { - assert(this.local_triangle_index_offset + i < mesh_lod.local_triangle_indices_count); let local_triangle_index = u32(mesh_lod.local_triangle_indices[this.local_triangle_index_offset + i]); - - assert(this.indirect_vertex_index_offset + local_triangle_index < mesh_lod.indirect_vertex_indices_count); return mesh_lod.indirect_vertex_indices[this.indirect_vertex_index_offset + local_triangle_index]; } diff --git a/Lorr/Engine/Resources/shaders/sky.slang b/Lorr/Engine/Resources/shaders/sky.slang index e6560169..6c65e443 100644 --- a/Lorr/Engine/Resources/shaders/sky.slang +++ b/Lorr/Engine/Resources/shaders/sky.slang @@ -12,12 +12,12 @@ public func from_unit_to_sub_uvs(f32x2 uv, f32x2 res) -> f32x2 { return (uv + 0.5 / res) * (res / (res + 1.0)); } -public func transmittance_params_to_lut_uv(in Atmosphere atmosphere, f32x2 p) -> f32x2 { - f32 h = std::safe_sqrt(atmosphere.atmos_radius * atmosphere.atmos_radius - atmosphere.planet_radius * atmosphere.planet_radius); - f32 rho = std::safe_sqrt(p.x * p.x - atmosphere.planet_radius * atmosphere.planet_radius); - f32 discriminant = p.x * p.x * (p.y * p.y - 1.0) + atmosphere.atmos_radius * atmosphere.atmos_radius; +public func transmittance_params_to_lut_uv(f32 atmos_radius, f32 planet_radius, f32x2 p) -> f32x2 { + f32 h = std::safe_sqrt(atmos_radius * atmos_radius - planet_radius * planet_radius); + f32 rho = std::safe_sqrt(p.x * p.x - planet_radius * planet_radius); + f32 discriminant = p.x * p.x * (p.y * p.y - 1.0) + atmos_radius * atmos_radius; f32 d = max(0.0, -p.x * p.y + std::safe_sqrt(discriminant)); - f32 d_min = atmosphere.atmos_radius - p.x; + f32 d_min = atmos_radius - p.x; f32 d_max = rho + h; f32 mu = (d - d_min) / (d_max - d_min); f32 r = rho / h; @@ -25,14 +25,27 @@ public func transmittance_params_to_lut_uv(in Atmosphere atmosphere, f32x2 p) -> return f32x2(mu, r); } -public func multiscattering_params_to_lut_uv(in Atmosphere atmosphere, f32 altitude, f32 cos_theta) -> f32x2 { - f32x2 uv = clamp(f32x2(cos_theta * 0.5 + 0.5, altitude / (atmosphere.atmos_radius - atmosphere.planet_radius)), 0.0, 1.0); - return from_unit_to_sub_uvs(uv, f32x2(atmosphere.multiscattering_lut_size.xy)); +public func multiscattering_params_to_lut_uv( + f32 atmos_radius, + f32 planet_radius, + i32x2 multiscattering_lut_size, + f32 altitude, + f32 cos_theta +) -> f32x2 { + f32x2 uv = clamp(f32x2(cos_theta * 0.5 + 0.5, altitude / (atmos_radius - planet_radius)), 0.0, 1.0); + return from_unit_to_sub_uvs(uv, f32x2(multiscattering_lut_size)); } -public func sky_view_params_to_lut_uv(in Atmosphere atmosphere, bool intersect_planet, f32 altitude, f32 view_zenith_cos_angle, f32x2 light_on_plane) - -> f32x2 { - f32 horizon = std::safe_sqrt(altitude * altitude - atmosphere.planet_radius * atmosphere.planet_radius); +public func sky_view_params_to_lut_uv( + f32 atmos_radius, + f32 planet_radius, + i32x2 sky_view_lut_size, + bool intersect_planet, + f32 altitude, + f32 view_zenith_cos_angle, + f32x2 light_on_plane +) -> f32x2 { + f32 horizon = std::safe_sqrt(altitude * altitude - planet_radius * planet_radius); f32 beta = acos(horizon / altitude); f32 zenith_horizon_angle = PI - beta; f32 view_zenith_angle = acos(view_zenith_cos_angle); @@ -52,12 +65,17 @@ public func sky_view_params_to_lut_uv(in Atmosphere atmosphere, bool intersect_p f32 theta = atan2(-light_on_plane.y, -light_on_plane.x); uv.x = (theta + PI) / (2.0 * PI); - return from_unit_to_sub_uvs(uv, f32x2(atmosphere.sky_view_lut_size.xy)); + return from_unit_to_sub_uvs(uv, f32x2(sky_view_lut_size)); } -public func uv_to_sky_view_lut_params(in Atmosphere atmosphere, f32x2 uv, f32 altitude) -> f32x3 { - uv = from_sub_uvs_to_unit(uv, f32x2(atmosphere.sky_view_lut_size.xy)); - f32 horizon = std::safe_sqrt(altitude * altitude - atmosphere.planet_radius * atmosphere.planet_radius); +public func uv_to_sky_view_lut_params( + f32 planet_radius, + i32x2 sky_view_lut_size, + f32x2 uv, + f32 altitude +) -> f32x3 { + uv = from_sub_uvs_to_unit(uv, f32x2(sky_view_lut_size)); + f32 horizon = std::safe_sqrt(altitude * altitude - planet_radius * planet_radius); f32 beta = acos(horizon / altitude); f32 zenith_horizon_angle = PI - beta; @@ -74,11 +92,11 @@ public func uv_to_sky_view_lut_params(in Atmosphere atmosphere, f32x2 uv, f32 al view_zenith_angle = zenith_horizon_angle + beta * coord; } - const f32 longitude_view_cos_angle = uv.x * TAU; - const f32 view_zenith_cos_angle = cos(view_zenith_angle); - const f32 view_zenith_sin_angle = std::safe_sqrt(1.0 - view_zenith_cos_angle * view_zenith_cos_angle) * (view_zenith_angle > 0.0 ? 1.0 : -1.0); - const f32 cos_longitude_view_cos_angle = cos(longitude_view_cos_angle); - const f32 sin_longitude_view_cos_angle = + let longitude_view_cos_angle = uv.x * TAU; + let view_zenith_cos_angle = cos(view_zenith_angle); + let view_zenith_sin_angle = std::safe_sqrt(1.0 - view_zenith_cos_angle * view_zenith_cos_angle) * (view_zenith_angle > 0.0 ? 1.0 : -1.0); + let cos_longitude_view_cos_angle = cos(longitude_view_cos_angle); + let sin_longitude_view_cos_angle = std::safe_sqrt(1.0 - cos_longitude_view_cos_angle * cos_longitude_view_cos_angle) * (longitude_view_cos_angle <= PI ? 1.0 : -1.0); return f32x3(view_zenith_sin_angle * cos_longitude_view_cos_angle, view_zenith_cos_angle, view_zenith_sin_angle * sin_longitude_view_cos_angle); } @@ -111,18 +129,18 @@ public struct MediumScattering { public f32x3 scattering_sum; public f32x3 extinction_sum; - [ForceInline] public __init(in Atmosphere atmosphere, f32 altitude) { - const f32 rayleigh_density = exp(-altitude / atmosphere.rayleigh_density); - const f32 mie_density = exp(-altitude / atmosphere.mie_density); - const f32 ozone_density = max(0.0, 1.0 - abs(altitude - atmosphere.ozone_height) / atmosphere.ozone_thickness); + [ForceInline] public __init(in Environment environment, f32 altitude) { + const f32 rayleigh_density = exp(-altitude / environment.atmos_rayleigh_density); + const f32 mie_density = exp(-altitude / environment.atmos_mie_density); + const f32 ozone_density = max(0.0, 1.0 - abs(altitude - environment.atmos_ozone_height) / environment.atmos_ozone_thickness); - this.rayleigh_scattering = atmosphere.rayleigh_scatter * rayleigh_density; + this.rayleigh_scattering = environment.atmos_rayleigh_scatter * rayleigh_density; this.rayleigh_extinction = this.rayleigh_scattering; // Rayleigh scattering doesn't have absorption behavior - this.mie_scattering = atmosphere.mie_scatter * mie_density; - this.mie_extinction = atmosphere.mie_extinction * mie_density; // Mie scattering doesn't have absorption behavior + this.mie_scattering = environment.atmos_mie_scatter * mie_density; + this.mie_extinction = environment.atmos_mie_extinction * mie_density; // Mie scattering doesn't have absorption behavior - this.ozone_absorption = atmosphere.ozone_absorption * ozone_density; + this.ozone_absorption = environment.atmos_ozone_absorption * ozone_density; this.ozone_extinction = this.ozone_absorption; this.scattering_sum = this.rayleigh_scattering + this.mie_scattering; @@ -159,16 +177,20 @@ public struct AtmosphereIntegrateInfo { public Optional> multiscattering_image = none; }; -public func integrate_single_scattered_luminance(in Atmosphere atmosphere, in Sampler lut_sampler, in AtmosphereIntegrateInfo info) -> AtmosphereLuminance { +public func integrate_single_scattered_luminance( + in Environment environment, + in Sampler lut_sampler, + in AtmosphereIntegrateInfo info +) -> AtmosphereLuminance { AtmosphereLuminance result = {}; - if (dot(info.eye_pos, info.eye_pos) <= atmosphere.planet_radius * atmosphere.planet_radius) { + if (dot(info.eye_pos, info.eye_pos) <= environment.atmos_planet_radius * environment.atmos_planet_radius) { return result; } - const let atmos_intersection = std::ray_sphere_intersect_nearest(info.eye_pos, info.eye_dir, atmosphere.atmos_radius); - const let planet_intersection = std::ray_sphere_intersect_nearest(info.eye_pos, info.eye_dir, atmosphere.planet_radius); - f32 integration_length = 0.0; + let atmos_intersection = std::ray_sphere_intersect_nearest(info.eye_pos, info.eye_dir, environment.atmos_atmos_radius); + let planet_intersection = std::ray_sphere_intersect_nearest(info.eye_pos, info.eye_dir, environment.atmos_planet_radius); + var integration_length = 0.0; if (!atmos_intersection.hasValue) { // No intersection return result; @@ -180,9 +202,9 @@ public func integrate_single_scattered_luminance(in Atmosphere atmosphere, in Sa } integration_length = min(integration_length, info.max_integration_length); - f32 sample_count = info.sampling.initial_sample_count; - f32 sample_count_floor = info.sampling.initial_sample_count; - f32 max_integration_length_floor = integration_length; + var sample_count = info.sampling.initial_sample_count; + var sample_count_floor = info.sampling.initial_sample_count; + var max_integration_length_floor = integration_length; if (info.sampling.variable_sample_count) { sample_count = lerp( info.sampling.min_sample_count, @@ -193,12 +215,12 @@ public func integrate_single_scattered_luminance(in Atmosphere atmosphere, in Sa max_integration_length_floor = integration_length * sample_count_floor / sample_count; } - f32 cos_theta = dot(info.sun_dir, info.eye_dir); - f32 rayleigh_phase = std::rayleigh_phase(cos_theta); - f32 mie_phase = std::henyey_greenstein_draine_phase(atmosphere.mie_asymmetry, cos_theta); + let cos_theta = dot(info.sun_dir, info.eye_dir); + let rayleigh_phase = std::rayleigh_phase(cos_theta); + let mie_phase = std::henyey_greenstein_draine_phase(environment.atmos_mie_asymmetry, cos_theta); - f32 step = 0.0; - f32 delta_step = integration_length / sample_count; + var step = 0.0; + var delta_step = integration_length / sample_count; for (f32 i = 0; i < sample_count; i += 1.0) { if (info.sampling.variable_sample_count) { f32 cur_step = (i + 0.0) / sample_count_floor; @@ -214,22 +236,25 @@ public func integrate_single_scattered_luminance(in Atmosphere atmosphere, in Sa step = integration_length * (i + 0.3) / sample_count; } - f32x3 step_pos = info.eye_pos + step * info.eye_dir; - f32 h = length(step_pos); - f32 altitude = h - atmosphere.planet_radius; - MediumScattering medium_info = MediumScattering(atmosphere, altitude); + let step_pos = info.eye_pos + step * info.eye_dir; + let h = length(step_pos); + let altitude = h - environment.atmos_planet_radius; + let medium_info = MediumScattering(environment, altitude); f32x3 up_vec = normalize(step_pos); f32x3 up_vec_scaled = PLANET_RADIUS_OFFSET * up_vec; - f32 earth_shadow = std::ray_sphere_intersect_nearest(step_pos - up_vec_scaled, info.sun_dir, atmosphere.planet_radius).hasValue ? 0.0 : 1.0; + f32 earth_shadow = std::ray_sphere_intersect_nearest( + step_pos - up_vec_scaled, info.sun_dir, environment.atmos_planet_radius).hasValue ? 0.0 : 1.0; f32 sun_theta = dot(info.sun_dir, up_vec); - f32x2 transmittance_uv = transmittance_params_to_lut_uv(atmosphere, f32x2(h, sun_theta)); + f32x2 transmittance_uv = transmittance_params_to_lut_uv( + environment.atmos_atmos_radius, environment.atmos_planet_radius, f32x2(h, sun_theta)); f32x3 sun_transmittance = info.transmittance_image.sample_mip(lut_sampler, transmittance_uv, 0.0).rgb; f32x3 MS = 0.0; if (info.multiscattering_image.hasValue) { - f32x2 multiscatter_uv = multiscattering_params_to_lut_uv(atmosphere, altitude, sun_theta); + f32x2 multiscatter_uv = multiscattering_params_to_lut_uv( + environment.atmos_atmos_radius, environment.atmos_planet_radius, environment.multiscattering_lut_size.xy, altitude, sun_theta); MS = info.multiscattering_image.value.sample_mip(lut_sampler, multiscatter_uv, 0.0).rgb; } @@ -263,27 +288,28 @@ public func integrate_single_scattered_luminance(in Atmosphere atmosphere, in Sa f32 sun_theta = dot(info.sun_dir, up_vec); f32 NoL = saturate(dot(normalize(info.sun_dir), normalize(up_vec))); - f32x2 transmittance_uv = transmittance_params_to_lut_uv(atmosphere, f32x2(h, sun_theta)); + f32x2 transmittance_uv = transmittance_params_to_lut_uv(environment.atmos_atmos_radius, environment.atmos_planet_radius, f32x2(h, sun_theta)); f32x3 sun_transmittance = info.transmittance_image.sample_mip(lut_sampler, transmittance_uv, 0.0).rgb; - result.luminance += info.sun_intensity * (sun_transmittance * result.transmittance * NoL * atmosphere.terrain_albedo / PI); + result.luminance += info.sun_intensity * (sun_transmittance * result.transmittance * NoL * environment.atmos_terrain_albedo / PI); } return result; } public func sample_aerial_perspective( - in Atmosphere atmosphere, in Image3D aerial_perspective_lut, in Sampler sampler, + i32x3 aerial_perspective_lut_size, f32x2 uv, - f32x3 camera_relative_pos + f32x3 camera_relative_pos, + f32 aerial_perspective_start_km ) -> f32x4 { - const let lut_size = f32x3(atmosphere.aerial_perspective_lut_size); + const let lut_size = f32x3(aerial_perspective_lut_size); const let per_slice_depth = f32(lut_size.x / lut_size.z); const let aerial_perspective_lut_depth = lut_size.z; - let relative_depth = max(0.0, length(camera_relative_pos) - atmosphere.aerial_perspective_start_km); + let relative_depth = max(0.0, length(camera_relative_pos) - aerial_perspective_start_km); let linear_slice = relative_depth * rcp(per_slice_depth); let linear_w = linear_slice * rcp(aerial_perspective_lut_depth); let non_linear_w = sqrt(linear_w); diff --git a/Lorr/Engine/Scene/ECSModule/ComponentWrapper.hh b/Lorr/Engine/Scene/ECSModule/ComponentWrapper.hh index 4b5701a8..5f68bf6a 100644 --- a/Lorr/Engine/Scene/ECSModule/ComponentWrapper.hh +++ b/Lorr/Engine/Scene/ECSModule/ComponentWrapper.hh @@ -8,6 +8,7 @@ namespace lr::ECS { struct ComponentWrapper { using Member = std::variant< std::monostate, + bool *, f32 *, i32 *, u32 *, @@ -59,7 +60,9 @@ struct ComponentWrapper { Member data = std::monostate{}; auto member_type = flecs::entity(world, member.type); - if (member_type == flecs::F32) { + if (member_type == flecs::Bool) { + data = reinterpret_cast(self.members_data + member.offset); + } else if (member_type == flecs::F32) { data = reinterpret_cast(self.members_data + member.offset); } else if (member_type == flecs::I32) { data = reinterpret_cast(self.members_data + member.offset); diff --git a/Lorr/Engine/Scene/ECSModule/CoreComponents.hh b/Lorr/Engine/Scene/ECSModule/CoreComponents.hh index 105532e3..9821a390 100644 --- a/Lorr/Engine/Scene/ECSModule/CoreComponents.hh +++ b/Lorr/Engine/Scene/ECSModule/CoreComponents.hh @@ -23,6 +23,7 @@ ECS_COMPONENT_END(); ECS_COMPONENT_BEGIN(Camera) ECS_COMPONENT_MEMBER(fov, f32, 90.0f) + ECS_COMPONENT_MEMBER(resolution, glm::vec2, {}) ECS_COMPONENT_MEMBER(aspect_ratio, f32, 1.777f) ECS_COMPONENT_MEMBER(near_clip, f32, 0.1f) ECS_COMPONENT_MEMBER(far_clip, f32, 1000.0f) @@ -42,30 +43,27 @@ ECS_COMPONENT_BEGIN(RenderingMesh) ECS_COMPONENT_MEMBER(mesh_index, u32, {}) ECS_COMPONENT_END(); -ECS_COMPONENT_BEGIN(DirectionalLight) - ECS_COMPONENT_MEMBER(direction, glm::vec2, { 90.0, 45.0 }) - ECS_COMPONENT_MEMBER(intensity, f32, 10.0) -ECS_COMPONENT_END(); - -ECS_COMPONENT_BEGIN(Atmosphere) - ECS_COMPONENT_MEMBER(rayleigh_scattering, glm::vec3, { 5.802f, 13.558f, 33.100f }) - ECS_COMPONENT_MEMBER(rayleigh_density, f32, 8.0) - ECS_COMPONENT_MEMBER(mie_scattering, glm::vec3, { 3.996f, 3.996f, 3.996f }) - ECS_COMPONENT_MEMBER(mie_density, f32, 1.2f) - ECS_COMPONENT_MEMBER(mie_extinction, f32, 4.44f) - ECS_COMPONENT_MEMBER(mie_asymmetry, f32, 3.6f) - ECS_COMPONENT_MEMBER(ozone_absorption, glm::vec3, { 0.650f, 1.881f, 0.085f }) - ECS_COMPONENT_MEMBER(ozone_height, f32, 25.0f) - ECS_COMPONENT_MEMBER(ozone_thickness, f32, 15.0f) - ECS_COMPONENT_MEMBER(aerial_perspective_start_km, f32, 8.0f) -ECS_COMPONENT_END(); - -ECS_COMPONENT_BEGIN(AutoExposure) - ECS_COMPONENT_MEMBER(min_exposure, f32, -6.0f) - ECS_COMPONENT_MEMBER(max_exposure, f32, 18.0f) - ECS_COMPONENT_MEMBER(adaptation_speed, f32, 1.1f) - ECS_COMPONENT_MEMBER(ISO, f32, 100.0f) - ECS_COMPONENT_MEMBER(K, f32, 12.5f) +ECS_COMPONENT_BEGIN(Environment) + ECS_COMPONENT_MEMBER(sun, bool, true) + ECS_COMPONENT_MEMBER(sun_direction, glm::vec2, { 90.0, 45.0 }) + ECS_COMPONENT_MEMBER(sun_intensity, f32, 10.0) + ECS_COMPONENT_MEMBER(atmos, bool, true) + ECS_COMPONENT_MEMBER(atmos_rayleigh_scattering, glm::vec3, { 5.802f, 13.558f, 33.100f }) + ECS_COMPONENT_MEMBER(atmos_rayleigh_density, f32, 8.0) + ECS_COMPONENT_MEMBER(atmos_mie_scattering, glm::vec3, { 3.996f, 3.996f, 3.996f }) + ECS_COMPONENT_MEMBER(atmos_mie_density, f32, 1.2f) + ECS_COMPONENT_MEMBER(atmos_mie_extinction, f32, 4.44f) + ECS_COMPONENT_MEMBER(atmos_mie_asymmetry, f32, 3.6f) + ECS_COMPONENT_MEMBER(atmos_ozone_absorption, glm::vec3, { 0.650f, 1.881f, 0.085f }) + ECS_COMPONENT_MEMBER(atmos_ozone_height, f32, 25.0f) + ECS_COMPONENT_MEMBER(atmos_ozone_thickness, f32, 15.0f) + ECS_COMPONENT_MEMBER(atmos_aerial_perspective_start_km, f32, 8.0f) + ECS_COMPONENT_MEMBER(eye_adaptation, bool, true) + ECS_COMPONENT_MEMBER(eye_min_exposure, f32, -6.0f) + ECS_COMPONENT_MEMBER(eye_max_exposure, f32, 18.0f) + ECS_COMPONENT_MEMBER(eye_adaptation_speed, f32, 1.1f) + ECS_COMPONENT_MEMBER(eye_iso, f32, 100.0f) + ECS_COMPONENT_MEMBER(eye_k, f32, 12.5f) ECS_COMPONENT_END(); // Any entity with this tag won't be serialized diff --git a/Lorr/Engine/Scene/GPUScene.hh b/Lorr/Engine/Scene/GPUScene.hh index 3a7806bc..2b9c98af 100644 --- a/Lorr/Engine/Scene/GPUScene.hh +++ b/Lorr/Engine/Scene/GPUScene.hh @@ -63,34 +63,37 @@ enum class CullFlags : u32 { All = MeshletFrustum | TriangleBackFace | MicroTriangles | Occlusion, }; -struct Sun { - alignas(4) glm::vec3 direction = {}; - alignas(4) f32 intensity = 10.0f; -}; - -constexpr static f32 CAMERA_SCALE_UNIT = 0.01; -constexpr static f32 INV_CAMERA_SCALE_UNIT = 1.0 / CAMERA_SCALE_UNIT; -constexpr static f32 PLANET_RADIUS_OFFSET = 0.001; - -struct Atmosphere { - alignas(4) glm::vec3 eye_position = {}; // this is camera pos but its always above planet_radius - - alignas(4) glm::vec3 rayleigh_scatter = { 0.005802f, 0.013558f, 0.033100f }; - alignas(4) f32 rayleigh_density = 8.0f; - - alignas(4) glm::vec3 mie_scatter = { 0.003996f, 0.003996f, 0.003996f }; - alignas(4) f32 mie_density = 1.2f; - alignas(4) f32 mie_extinction = 0.004440f; - alignas(4) f32 mie_asymmetry = 3.6f; - - alignas(4) glm::vec3 ozone_absorption = { 0.000650f, 0.001881f, 0.000085f }; - alignas(4) f32 ozone_height = 25.0f; - alignas(4) f32 ozone_thickness = 15.0f; - - alignas(4) glm::vec3 terrain_albedo = { 0.3f, 0.3f, 0.3f }; - alignas(4) f32 planet_radius = 6360.0f; - alignas(4) f32 atmos_radius = 6460.0f; - alignas(4) f32 aerial_perspective_start_km = 8.0f; +enum EnvironmentFlags : u32 { + None = 0, + HasSun = 1 << 0, + HasAtmosphere = 1 << 1, + HasEyeAdaptation = 1 << 2, +}; + +struct Environment { + alignas(4) u32 flags = EnvironmentFlags::None; + // Sun + alignas(4) glm::vec3 sun_direction = {}; + alignas(4) f32 sun_intensity = 10.0f; + // Atmosphere + alignas(4) glm::vec3 atmos_rayleigh_scatter = { 0.005802f, 0.013558f, 0.033100f }; + alignas(4) f32 atmos_rayleigh_density = 8.0f; + alignas(4) glm::vec3 atmos_mie_scatter = { 0.003996f, 0.003996f, 0.003996f }; + alignas(4) f32 atmos_mie_density = 1.2f; + alignas(4) f32 atmos_mie_extinction = 0.004440f; + alignas(4) f32 atmos_mie_asymmetry = 3.6f; + alignas(4) glm::vec3 atmos_ozone_absorption = { 0.000650f, 0.001881f, 0.000085f }; + alignas(4) f32 atmos_ozone_height = 25.0f; + alignas(4) f32 atmos_ozone_thickness = 15.0f; + alignas(4) glm::vec3 atmos_terrain_albedo = { 0.3f, 0.3f, 0.3f }; + alignas(4) f32 atmos_planet_radius = 6360.0f; + alignas(4) f32 atmos_atmos_radius = 6460.0f; + alignas(4) f32 atmos_aerial_perspective_start_km = 8.0f; + // Eye adaptation + alignas(4) f32 eye_min_exposure = -6.0f; + alignas(4) f32 eye_max_exposure = 18.0f; + alignas(4) f32 eye_adaptation_speed = 1.1f; + alignas(4) f32 eye_ISO_K = 100.0f / 12.5f; alignas(4) vuk::Extent3D transmittance_lut_size = {}; alignas(4) vuk::Extent3D sky_view_lut_size = {}; @@ -98,6 +101,10 @@ struct Atmosphere { alignas(4) vuk::Extent3D aerial_perspective_lut_size = {}; }; +constexpr static f32 CAMERA_SCALE_UNIT = 0.01; +constexpr static f32 INV_CAMERA_SCALE_UNIT = 1.0 / CAMERA_SCALE_UNIT; +constexpr static f32 PLANET_RADIUS_OFFSET = 0.001; + struct Camera { alignas(4) glm::mat4 projection_mat = {}; alignas(4) glm::mat4 view_mat = {}; @@ -211,15 +218,8 @@ constexpr static u32 HISTOGRAM_THREADS_Y = 16; constexpr static u32 HISTOGRAM_BIN_COUNT = HISTOGRAM_THREADS_X * HISTOGRAM_THREADS_Y; struct HistogramLuminance { - alignas(4) f32 adapted_luminance; - alignas(4) f32 exposure; -}; - -struct HistogramInfo { - alignas(4) f32 min_exposure = -6.0f; - alignas(4) f32 max_exposure = 18.0f; - alignas(4) f32 adaptation_speed = 1.1f; - alignas(4) f32 ISO_K = 100.0f / 12.5f; + f32 adapted_luminance = 0.0f; + f32 exposure = 0.0f; }; } // namespace lr::GPU diff --git a/Lorr/Engine/Scene/Scene.cc b/Lorr/Engine/Scene/Scene.cc index 0d64b6db..2e79f8bf 100644 --- a/Lorr/Engine/Scene/Scene.cc +++ b/Lorr/Engine/Scene/Scene.cc @@ -1,6 +1,8 @@ #include "Engine/Scene/Scene.hh" -#include "Engine/Core/Application.hh" +#include "Engine/Asset/Asset.hh" + +#include "Engine/Core/App.hh" #include "Engine/Memory/Stack.hh" @@ -146,10 +148,10 @@ auto Scene::destroy(this Scene &self) -> void { }; self.root.children([&](flecs::entity e) { visit_child(e); }); - auto &app = Application::get(); + auto &asset_man = App::mod(); for (const auto &uuid : unloading_assets) { - if (uuid && app.asset_man.get_asset(uuid)) { - app.asset_man.unload_asset(uuid); + if (uuid && asset_man.get_asset(uuid)) { + asset_man.unload_asset(uuid); } } @@ -296,9 +298,9 @@ auto Scene::import_from_file(this Scene &self, const fs::path &path) -> bool { LOG_TRACE("Loading scene {} with {} assets...", self.name, requested_assets.size()); for (const auto &uuid : requested_assets) { - auto &app = Application::get(); - if (uuid && app.asset_man.get_asset(uuid)) { - app.asset_man.load_asset(uuid); + auto &asset_man = App::mod(); + if (uuid && asset_man.get_asset(uuid)) { + asset_man.load_asset(uuid); } } @@ -443,19 +445,20 @@ auto Scene::create_editor_camera(this Scene &self) -> void { auto Scene::create_model_entity(this Scene &self, UUID &importing_model_uuid) -> flecs::entity { ZoneScoped; - auto &app = Application::get(); + auto &asset_man = App::mod(); + // sanity check - if (!app.asset_man.get_asset(importing_model_uuid)) { + if (!asset_man.get_asset(importing_model_uuid)) { LOG_ERROR("Cannot import an invalid model '{}' into the scene!", importing_model_uuid.str()); return {}; } // acquire model - if (!app.asset_man.load_model(importing_model_uuid)) { + if (!asset_man.load_model(importing_model_uuid)) { return {}; } - auto *imported_model = app.asset_man.get_model(importing_model_uuid); + auto *imported_model = asset_man.get_model(importing_model_uuid); auto &default_scene = imported_model->scenes[imported_model->default_scene_index]; auto root_entity = self.create_entity(self.find_entity(default_scene.name) ? std::string{} : default_scene.name); root_entity.child_of(self.root); @@ -518,97 +521,6 @@ auto Scene::find_entity(this Scene &self, u32 transform_index) -> flecs::entity return {}; } -auto Scene::render(this Scene &self, SceneRenderer &renderer, SceneRenderInfo &info) -> vuk::Value { - ZoneScoped; - - // clang-format off - auto camera_query = self.get_world() - .query_builder() - .build(); - auto rendering_meshes_query = self.get_world() - .query_builder() - .build(); - auto directional_light_query = self.get_world() - .query_builder() - .build(); - // clang-format on - - ls::option active_camera_data = ls::nullopt; - camera_query.each([&](flecs::entity, ECS::Transform &t, ECS::Camera &c, ECS::ActiveCamera) { - auto projection_mat = glm::perspectiveRH_ZO(glm::radians(c.fov), c.aspect_ratio, c.far_clip, c.near_clip); - projection_mat[1][1] *= -1; - - auto translation_mat = glm::translate(glm::mat4(1.0f), -t.position); - auto rotation_mat = glm::mat4_cast(Math::quat_dir(t.rotation)); - auto view_mat = rotation_mat * translation_mat; - - auto &camera_data = active_camera_data.emplace(); - camera_data.projection_mat = projection_mat; - camera_data.view_mat = view_mat; - camera_data.projection_view_mat = camera_data.projection_mat * camera_data.view_mat; - camera_data.inv_view_mat = glm::inverse(camera_data.view_mat); - camera_data.inv_projection_view_mat = glm::inverse(camera_data.projection_view_mat); - camera_data.position = t.position; - camera_data.near_clip = c.near_clip; - camera_data.far_clip = c.far_clip; - camera_data.resolution = glm::vec2(static_cast(info.extent.width), static_cast(info.extent.height)); - camera_data.acceptable_lod_error = c.acceptable_lod_error; - camera_data.frustum_projection_view_mat = c.frustum_projection_view_mat; - c.frustum_projection_view_mat = camera_data.projection_view_mat; - }); - - ls::option sun_data = ls::nullopt; - ls::option atmos_data = ls::nullopt; - ls::option histogram_data = ls::nullopt; - directional_light_query.each([&](flecs::entity e, ECS::DirectionalLight &light) { - auto light_dir_rad = glm::radians(light.direction); - - auto &sun = sun_data.emplace(); - sun.direction.x = glm::cos(light_dir_rad.x) * glm::sin(light_dir_rad.y); - sun.direction.y = glm::sin(light_dir_rad.x) * glm::sin(light_dir_rad.y); - sun.direction.z = glm::cos(light_dir_rad.y); - sun.intensity = light.intensity; - - if (e.has()) { - const auto &atmos_info = *e.get(); - auto &atmos = atmos_data.emplace(); - atmos.rayleigh_scatter = atmos_info.rayleigh_scattering * 1e-3f; - atmos.rayleigh_density = atmos_info.rayleigh_density; - atmos.mie_scatter = atmos_info.mie_scattering * 1e-3f; - atmos.mie_density = atmos_info.mie_density; - atmos.mie_extinction = atmos_info.mie_extinction * 1e-3f; - atmos.mie_asymmetry = atmos_info.mie_asymmetry; - atmos.ozone_absorption = atmos_info.ozone_absorption * 1e-3f; - atmos.ozone_height = atmos_info.ozone_height; - atmos.ozone_thickness = atmos_info.ozone_thickness; - atmos.aerial_perspective_start_km = atmos_info.aerial_perspective_start_km; - - f32 eye_altitude = active_camera_data->position.y * GPU::CAMERA_SCALE_UNIT; - eye_altitude += atmos.planet_radius + GPU::PLANET_RADIUS_OFFSET; - atmos.eye_position = glm::vec3(0.0f, eye_altitude, 0.0f); - } - - if (e.has()) { - const auto &auto_exposure = *e.get(); - auto &histogram = histogram_data.emplace(); - histogram.min_exposure = auto_exposure.min_exposure; - histogram.max_exposure = auto_exposure.max_exposure; - histogram.adaptation_speed = auto_exposure.adaptation_speed; - histogram.ISO_K = auto_exposure.ISO / auto_exposure.K; - } - }); - - auto prepared_frame = self.prepare_frame(renderer); - - info.sun = sun_data; - info.atmosphere = atmos_data; - info.camera = active_camera_data; - info.histogram_info = histogram_data; - info.cull_flags = self.cull_flags; - - return renderer.render(info, prepared_frame); -} - auto Scene::tick(this Scene &self, f32 delta_time) -> bool { return self.world->progress(delta_time); } @@ -708,7 +620,73 @@ auto Scene::get_cull_flags(this Scene &self) -> GPU::CullFlags & { auto Scene::prepare_frame(this Scene &self, SceneRenderer &renderer) -> PreparedFrame { ZoneScoped; - auto &app = Application::get(); + auto &asset_man = App::mod(); + + // clang-format off + auto camera_query = self.get_world() + .query_builder() + .build(); + auto rendering_meshes_query = self.get_world() + .query_builder() + .build(); + auto environment_query = self.get_world() + .query_builder() + .build(); + // clang-format on + + ls::option active_camera_data = ls::nullopt; + camera_query.each([&active_camera_data](flecs::entity, ECS::Transform &t, ECS::Camera &c, ECS::ActiveCamera) { + auto projection_mat = glm::perspectiveRH_ZO(glm::radians(c.fov), c.aspect_ratio, c.far_clip, c.near_clip); + projection_mat[1][1] *= -1; + + auto translation_mat = glm::translate(glm::mat4(1.0f), -t.position); + auto rotation_mat = glm::mat4_cast(Math::quat_dir(t.rotation)); + auto view_mat = rotation_mat * translation_mat; + + auto &camera_data = active_camera_data.emplace(GPU::Camera{}); + camera_data.projection_mat = projection_mat; + camera_data.view_mat = view_mat; + camera_data.projection_view_mat = camera_data.projection_mat * camera_data.view_mat; + camera_data.inv_view_mat = glm::inverse(camera_data.view_mat); + camera_data.inv_projection_view_mat = glm::inverse(camera_data.projection_view_mat); + camera_data.position = t.position; + camera_data.near_clip = c.near_clip; + camera_data.far_clip = c.far_clip; + camera_data.resolution = c.resolution; + camera_data.acceptable_lod_error = c.acceptable_lod_error; + camera_data.frustum_projection_view_mat = c.frustum_projection_view_mat; + c.frustum_projection_view_mat = camera_data.projection_view_mat; + }); + + ls::option environment_data = ls::nullopt; + environment_query.each([&environment_data](flecs::entity, ECS::Environment &environment_comp) { + auto &environment = environment_data.emplace(GPU::Environment{}); + environment.flags |= environment_comp.sun ? GPU::EnvironmentFlags::HasSun : 0; + environment.flags |= environment_comp.atmos ? GPU::EnvironmentFlags::HasAtmosphere : 0; + environment.flags |= environment_comp.eye_adaptation ? GPU::EnvironmentFlags::HasEyeAdaptation : 0; + + auto light_dir_rad = glm::radians(environment_comp.sun_direction); + environment.sun_direction = { + glm::cos(light_dir_rad.x) * glm::sin(light_dir_rad.y), + glm::sin(light_dir_rad.x) * glm::sin(light_dir_rad.y), + glm::cos(light_dir_rad.y), + }; + environment.sun_intensity = environment_comp.sun_intensity; + environment.atmos_rayleigh_scatter = environment_comp.atmos_rayleigh_scattering * 1e-3f; + environment.atmos_rayleigh_density = environment_comp.atmos_rayleigh_density; + environment.atmos_mie_scatter = environment_comp.atmos_mie_scattering * 1e-3f; + environment.atmos_mie_density = environment_comp.atmos_mie_density; + environment.atmos_mie_extinction = environment_comp.atmos_mie_extinction * 1e-3f; + environment.atmos_mie_asymmetry = environment_comp.atmos_mie_asymmetry; + environment.atmos_ozone_absorption = environment_comp.atmos_ozone_absorption * 1e-3f; + environment.atmos_ozone_height = environment_comp.atmos_ozone_height; + environment.atmos_ozone_thickness = environment_comp.atmos_ozone_thickness; + environment.atmos_aerial_perspective_start_km = environment_comp.atmos_aerial_perspective_start_km; + environment.eye_min_exposure = environment_comp.eye_min_exposure; + environment.eye_max_exposure = environment_comp.eye_max_exposure; + environment.eye_adaptation_speed = environment_comp.eye_adaptation_speed; + environment.eye_ISO_K = environment_comp.eye_iso / environment_comp.eye_k; + }); auto max_meshlet_instance_count = 0_u32; auto gpu_meshes = std::vector(); @@ -716,7 +694,7 @@ auto Scene::prepare_frame(this Scene &self, SceneRenderer &renderer) -> Prepared if (self.models_dirty) { for (const auto &[rendering_mesh, transform_ids] : self.rendering_meshes_map) { - auto *model = app.asset_man.get_model(rendering_mesh.n0); + auto *model = asset_man.get_model(rendering_mesh.n0); const auto &mesh = model->meshes[rendering_mesh.n1]; for (auto primitive_index : mesh.primitive_indices) { @@ -745,19 +723,19 @@ auto Scene::prepare_frame(this Scene &self, SceneRenderer &renderer) -> Prepared } auto uuid_to_image_index = [&](const UUID &uuid) -> ls::option { - if (!app.asset_man.is_texture_loaded(uuid)) { + if (!asset_man.is_texture_loaded(uuid)) { return ls::nullopt; } - auto *texture = app.asset_man.get_texture(uuid); + auto *texture = asset_man.get_texture(uuid); return texture->image_view.index(); }; - auto dirty_material_ids = app.asset_man.get_dirty_material_ids(); + auto dirty_material_ids = asset_man.get_dirty_material_ids(); auto gpu_materials = std::vector(dirty_material_ids.size()); auto dirty_material_indices = std::vector(dirty_material_ids.size()); for (const auto &[gpu_material, index, id] : std::views::zip(gpu_materials, dirty_material_indices, dirty_material_ids)) { - const auto *material = app.asset_man.get_material(id); + const auto *material = asset_man.get_material(id); auto albedo_image_index = uuid_to_image_index(material->albedo_texture); auto normal_image_index = uuid_to_image_index(material->normal_texture); auto emissive_image_index = uuid_to_image_index(material->emissive_texture); @@ -767,7 +745,7 @@ auto Scene::prepare_frame(this Scene &self, SceneRenderer &renderer) -> Prepared auto flags = GPU::MaterialFlag::None; if (albedo_image_index.has_value()) { - auto *texture = app.asset_man.get_texture(material->albedo_texture); + auto *texture = asset_man.get_texture(material->albedo_texture); sampler_index = texture->sampler.index(); flags |= GPU::MaterialFlag::HasAlbedoImage; } @@ -802,6 +780,8 @@ auto Scene::prepare_frame(this Scene &self, SceneRenderer &renderer) -> Prepared .gpu_materials = gpu_materials, .gpu_meshes = gpu_meshes, .gpu_mesh_instances = gpu_mesh_instances, + .environment = environment_data.value_or(GPU::Environment{}), + .camera = active_camera_data.value_or(GPU::Camera{}), }; auto prepared_frame = renderer.prepare_frame(prepare_info); diff --git a/Lorr/Engine/Scene/Scene.hh b/Lorr/Engine/Scene/Scene.hh index 124ab661..74848e93 100644 --- a/Lorr/Engine/Scene/Scene.hh +++ b/Lorr/Engine/Scene/Scene.hh @@ -79,7 +79,7 @@ public: auto find_entity(this Scene &, std::string_view name) -> flecs::entity; auto find_entity(this Scene &, u32 transform_index) -> flecs::entity; - auto render(this Scene &, SceneRenderer &renderer, SceneRenderInfo &info) -> vuk::Value; + auto prepare_frame(this Scene &, SceneRenderer &renderer) -> PreparedFrame; auto tick(this Scene &, f32 delta_time) -> bool; auto set_name(this Scene &, const std::string &name) -> void; @@ -94,8 +94,6 @@ public: auto get_cull_flags(this Scene &) -> GPU::CullFlags &; private: - auto prepare_frame(this Scene &, SceneRenderer &renderer) -> PreparedFrame; - auto add_transform(this Scene &, flecs::entity entity) -> GPU::TransformID; auto remove_transform(this Scene &, flecs::entity entity) -> void; diff --git a/Lorr/Engine/Scene/SceneRenderer.cc b/Lorr/Engine/Scene/SceneRenderer.cc index 5f81ab3a..9c4fff7a 100644 --- a/Lorr/Engine/Scene/SceneRenderer.cc +++ b/Lorr/Engine/Scene/SceneRenderer.cc @@ -1,6 +1,10 @@ #include "Engine/Scene/SceneRenderer.hh" -#include "Engine/Core/Application.hh" +#include "Engine/Asset/Asset.hh" + +#include "Engine/Core/App.hh" + +#include "Engine/Graphics/VulkanDevice.hh" namespace lr { enum BindlessDescriptorLayout : u32 { @@ -8,30 +12,17 @@ enum BindlessDescriptorLayout : u32 { SampledImages = 1, }; -auto SceneRenderer::init(this SceneRenderer &self, Device *device) -> bool { - self.device = device; - - self.create_persistent_resources(); - return true; -} - -auto SceneRenderer::destroy(this SceneRenderer &self) -> void { +auto SceneRenderer::init(this SceneRenderer &self) -> bool { ZoneScoped; - self.cleanup(); -} - -auto SceneRenderer::create_persistent_resources(this SceneRenderer &self) -> void { - ZoneScoped; - - auto &app = Application::get(); - auto &asset_man = app.asset_man; - auto &transfer_man = app.device.transfer_man(); - auto &bindless_descriptor_set = app.device.get_descriptor_set(); + auto &device = App::mod(); + auto &bindless_descriptor_set = device.get_descriptor_set(); + auto &transfer_man = device.transfer_man(); + auto &asset_man = App::mod(); auto shaders_root = asset_man.asset_root_path(AssetType::Shader); // ── EDITOR ────────────────────────────────────────────────────────── - auto default_slang_session = self.device->new_slang_session({ + auto default_slang_session = device.new_slang_session({ .definitions = { #ifdef LS_DEBUG { "ENABLE_ASSERTIONS", "1" }, @@ -49,13 +40,13 @@ auto SceneRenderer::create_persistent_resources(this SceneRenderer &self) -> voi .module_name = "passes.editor_grid", .entry_points = { "vs_main", "fs_main" }, }; - Pipeline::create(*self.device, default_slang_session, editor_grid_pipeline_info).value(); + Pipeline::create(device, default_slang_session, editor_grid_pipeline_info).value(); auto editor_mousepick_pipeline_info = PipelineCompileInfo{ .module_name = "passes.editor_mousepick", .entry_points = { "cs_main" }, }; - Pipeline::create(*self.device, default_slang_session, editor_mousepick_pipeline_info).value(); + Pipeline::create(device, default_slang_session, editor_mousepick_pipeline_info).value(); // ── SKY ───────────────────────────────────────────────────────────── auto sky_transmittance_lut_info = ImageInfo{ .format = vuk::Format::eR16G16B16A16Sfloat, @@ -64,12 +55,12 @@ auto SceneRenderer::create_persistent_resources(this SceneRenderer &self) -> voi .extent = { .width = 256, .height = 64, .depth = 1 }, .name = "Sky Transmittance", }; - std::tie(self.sky_transmittance_lut, self.sky_transmittance_lut_view) = Image::create_with_view(*self.device, sky_transmittance_lut_info).value(); + std::tie(self.sky_transmittance_lut, self.sky_transmittance_lut_view) = Image::create_with_view(device, sky_transmittance_lut_info).value(); auto sky_transmittance_pipeline_info = PipelineCompileInfo{ .module_name = "passes.sky_transmittance", .entry_points = { "cs_main" }, }; - Pipeline::create(*self.device, default_slang_session, sky_transmittance_pipeline_info).value(); + Pipeline::create(device, default_slang_session, sky_transmittance_pipeline_info).value(); auto sky_multiscatter_lut_info = ImageInfo{ .format = vuk::Format::eR16G16B16A16Sfloat, @@ -78,127 +69,130 @@ auto SceneRenderer::create_persistent_resources(this SceneRenderer &self) -> voi .extent = { .width = 32, .height = 32, .depth = 1 }, .name = "Sky Multiscatter LUT", }; - std::tie(self.sky_multiscatter_lut, self.sky_multiscatter_lut_view) = Image::create_with_view(*self.device, sky_multiscatter_lut_info).value(); + std::tie(self.sky_multiscatter_lut, self.sky_multiscatter_lut_view) = Image::create_with_view(device, sky_multiscatter_lut_info).value(); auto sky_multiscatter_pipeline_info = PipelineCompileInfo{ .module_name = "passes.sky_multiscattering", .entry_points = { "cs_main" }, }; - Pipeline::create(*self.device, default_slang_session, sky_multiscatter_pipeline_info).value(); + Pipeline::create(device, default_slang_session, sky_multiscatter_pipeline_info).value(); auto sky_view_pipeline_info = PipelineCompileInfo{ .module_name = "passes.sky_view", .entry_points = { "cs_main" }, }; - Pipeline::create(*self.device, default_slang_session, sky_view_pipeline_info).value(); + Pipeline::create(device, default_slang_session, sky_view_pipeline_info).value(); auto sky_aerial_perspective_pipeline_info = PipelineCompileInfo{ .module_name = "passes.sky_aerial_perspective", .entry_points = { "cs_main" }, }; - Pipeline::create(*self.device, default_slang_session, sky_aerial_perspective_pipeline_info).value(); + Pipeline::create(device, default_slang_session, sky_aerial_perspective_pipeline_info).value(); auto sky_final_pipeline_info = PipelineCompileInfo{ .module_name = "passes.sky_final", .entry_points = { "vs_main", "fs_main" }, }; - Pipeline::create(*self.device, default_slang_session, sky_final_pipeline_info).value(); + Pipeline::create(device, default_slang_session, sky_final_pipeline_info).value(); // ── VISBUFFER ─────────────────────────────────────────────────────── auto generate_cull_commands_pipeline_info = PipelineCompileInfo{ .module_name = "passes.generate_cull_commands", .entry_points = { "cs_main" }, }; - Pipeline::create(*self.device, default_slang_session, generate_cull_commands_pipeline_info).value(); + Pipeline::create(device, default_slang_session, generate_cull_commands_pipeline_info).value(); auto vis_select_lods_pipeline_info = PipelineCompileInfo{ .module_name = "passes.select_lods", .entry_points = { "cs_main" }, }; - Pipeline::create(*self.device, default_slang_session, vis_select_lods_pipeline_info).value(); + Pipeline::create(device, default_slang_session, vis_select_lods_pipeline_info).value(); auto vis_cull_meshlets_pipeline_info = PipelineCompileInfo{ .module_name = "passes.cull_meshlets", .entry_points = { "cs_main" }, }; - Pipeline::create(*self.device, default_slang_session, vis_cull_meshlets_pipeline_info).value(); + Pipeline::create(device, default_slang_session, vis_cull_meshlets_pipeline_info).value(); auto vis_cull_triangles_pipeline_info = PipelineCompileInfo{ .module_name = "passes.cull_triangles", .entry_points = { "cs_main" }, }; - Pipeline::create(*self.device, default_slang_session, vis_cull_triangles_pipeline_info).value(); + Pipeline::create(device, default_slang_session, vis_cull_triangles_pipeline_info).value(); auto vis_encode_pipeline_info = PipelineCompileInfo{ .module_name = "passes.visbuffer_encode", .entry_points = { "vs_main", "fs_main" }, }; - Pipeline::create(*self.device, default_slang_session, vis_encode_pipeline_info, bindless_descriptor_set).value(); + Pipeline::create(device, default_slang_session, vis_encode_pipeline_info, bindless_descriptor_set).value(); auto vis_clear_pipeline_info = PipelineCompileInfo{ .module_name = "passes.visbuffer_clear", .entry_points = { "cs_main" }, }; - Pipeline::create(*self.device, default_slang_session, vis_clear_pipeline_info).value(); + Pipeline::create(device, default_slang_session, vis_clear_pipeline_info).value(); auto vis_decode_pipeline_info = PipelineCompileInfo{ .module_name = "passes.visbuffer_decode", .entry_points = { "vs_main", "fs_main" }, }; - Pipeline::create(*self.device, default_slang_session, vis_decode_pipeline_info, bindless_descriptor_set).value(); + Pipeline::create(device, default_slang_session, vis_decode_pipeline_info, bindless_descriptor_set).value(); // ── PBR ───────────────────────────────────────────────────────────── auto pbr_basic_pipeline_info = PipelineCompileInfo{ .module_name = "passes.brdf", .entry_points = { "vs_main", "fs_main" }, }; - Pipeline::create(*self.device, default_slang_session, pbr_basic_pipeline_info).value(); + Pipeline::create(device, default_slang_session, pbr_basic_pipeline_info).value(); // ── POST PROCESS ──────────────────────────────────────────────────── auto histogram_generate_pipeline_info = PipelineCompileInfo{ .module_name = "passes.histogram_generate", .entry_points = { "cs_main" }, }; - Pipeline::create(*self.device, default_slang_session, histogram_generate_pipeline_info).value(); + Pipeline::create(device, default_slang_session, histogram_generate_pipeline_info).value(); auto histogram_average_pipeline_info = PipelineCompileInfo{ .module_name = "passes.histogram_average", .entry_points = { "cs_main" }, }; - Pipeline::create(*self.device, default_slang_session, histogram_average_pipeline_info).value(); + Pipeline::create(device, default_slang_session, histogram_average_pipeline_info).value(); auto tonemap_pipeline_info = PipelineCompileInfo{ .module_name = "passes.tonemap", .entry_points = { "vs_main", "fs_main" }, }; - Pipeline::create(*self.device, default_slang_session, tonemap_pipeline_info).value(); + Pipeline::create(device, default_slang_session, tonemap_pipeline_info).value(); auto debug_pipeline_info = PipelineCompileInfo{ .module_name = "passes.debug", .entry_points = { "vs_main", "fs_main" }, }; - Pipeline::create(*self.device, default_slang_session, debug_pipeline_info).value(); + Pipeline::create(device, default_slang_session, debug_pipeline_info).value(); auto copy_pipeline_info = PipelineCompileInfo{ .module_name = "passes.copy", .entry_points = { "cs_main" }, }; - Pipeline::create(*self.device, default_slang_session, copy_pipeline_info).value(); + Pipeline::create(device, default_slang_session, copy_pipeline_info).value(); // ── FFX ───────────────────────────────────────────────────────────── auto hiz_pipeline_info = PipelineCompileInfo{ .module_name = "passes.hiz", .entry_points = { "cs_main" }, }; - Pipeline::create(*self.device, default_slang_session, hiz_pipeline_info).value(); + Pipeline::create(device, default_slang_session, hiz_pipeline_info).value(); // ── SKY LUTS ──────────────────────────────────────────────────────── - auto temp_atmos_info = GPU::Atmosphere{}; - temp_atmos_info.transmittance_lut_size = self.sky_transmittance_lut_view.extent(); - temp_atmos_info.multiscattering_lut_size = self.sky_multiscatter_lut_view.extent(); - auto temp_atmos = transfer_man.scratch_buffer(temp_atmos_info); - - auto transmittance_lut_pass = - vuk::make_pass("transmittance lut", [](vuk::CommandBuffer &cmd_list, VUK_IA(vuk::eComputeRW) dst, VUK_BA(vuk::eComputeRead) atmos) { + auto temp_environment_info = GPU::Environment{}; + temp_environment_info.transmittance_lut_size = self.sky_transmittance_lut_view.extent(); + temp_environment_info.multiscattering_lut_size = self.sky_multiscatter_lut_view.extent(); + auto temp_environment = transfer_man.scratch_buffer(temp_environment_info); + + auto transmittance_lut_pass = vuk::make_pass( + "transmittance lut", + [](vuk::CommandBuffer &cmd_list, // + VUK_IA(vuk::eComputeRW) dst, + VUK_BA(vuk::eComputeRead) atmos) { cmd_list // .bind_compute_pipeline("passes.sky_transmittance") .bind_image(0, 0, dst) @@ -206,16 +200,16 @@ auto SceneRenderer::create_persistent_resources(this SceneRenderer &self) -> voi .dispatch_invocations_per_pixel(dst); return std::make_tuple(dst, atmos); - }); - - auto transmittance_lut_attachment = - self.sky_transmittance_lut_view.discard(*self.device, "sky transmittance lut", vuk::ImageUsageFlagBits::eStorage); + } + ); - std::tie(transmittance_lut_attachment, temp_atmos) = transmittance_lut_pass(std::move(transmittance_lut_attachment), std::move(temp_atmos)); + auto transmittance_lut_attachment = self.sky_transmittance_lut_view.discard(device, "sky transmittance lut", vuk::ImageUsageFlagBits::eStorage); + std::tie(transmittance_lut_attachment, temp_environment) = + transmittance_lut_pass(std::move(transmittance_lut_attachment), std::move(temp_environment)); auto multiscatter_lut_pass = vuk::make_pass( "multiscatter lut", - [](vuk::CommandBuffer &cmd_list, + [](vuk::CommandBuffer &cmd_list, // VUK_IA(vuk::eComputeSampled) sky_transmittance_lut, VUK_IA(vuk::eComputeRW) sky_multiscatter_lut, VUK_BA(vuk::eComputeRead) atmos) { @@ -231,30 +225,37 @@ auto SceneRenderer::create_persistent_resources(this SceneRenderer &self) -> voi } ); - auto multiscatter_lut_attachment = - self.sky_multiscatter_lut_view.discard(*self.device, "sky multiscatter lut", vuk::ImageUsageFlagBits::eStorage); - - std::tie(transmittance_lut_attachment, multiscatter_lut_attachment, temp_atmos) = - multiscatter_lut_pass(std::move(transmittance_lut_attachment), std::move(multiscatter_lut_attachment), std::move(temp_atmos)); + auto multiscatter_lut_attachment = self.sky_multiscatter_lut_view.discard(device, "sky multiscatter lut", vuk::ImageUsageFlagBits::eStorage); + std::tie(transmittance_lut_attachment, multiscatter_lut_attachment, temp_environment) = + multiscatter_lut_pass(std::move(transmittance_lut_attachment), std::move(multiscatter_lut_attachment), std::move(temp_environment)); transmittance_lut_attachment = transmittance_lut_attachment.as_released(vuk::eComputeSampled, vuk::DomainFlagBits::eGraphicsQueue); multiscatter_lut_attachment = multiscatter_lut_attachment.as_released(vuk::eComputeSampled, vuk::DomainFlagBits::eGraphicsQueue); transfer_man.wait_on(std::move(transmittance_lut_attachment)); transfer_man.wait_on(std::move(multiscatter_lut_attachment)); - self.exposure_buffer = Buffer::create(*self.device, sizeof(GPU::HistogramLuminance)).value(); - vuk::fill(vuk::acquire_buf("exposure", *self.device->buffer(self.exposure_buffer.id()), vuk::eNone), 0); + self.histogram_luminance_buffer = Buffer::create(device, sizeof(GPU::HistogramLuminance)).value(); + vuk::fill(vuk::acquire_buf("histogram luminance", *device.buffer(self.histogram_luminance_buffer.id()), vuk::eNone), 0); + + return true; +} + +auto SceneRenderer::destroy(this SceneRenderer &self) -> void { + ZoneScoped; + + self.cleanup(); } auto SceneRenderer::prepare_frame(this SceneRenderer &self, FramePrepareInfo &info) -> PreparedFrame { ZoneScoped; - auto &transfer_man = self.device->transfer_man(); + auto &device = App::mod(); + auto &transfer_man = device.transfer_man(); auto prepared_frame = PreparedFrame{}; if (!info.dirty_transform_ids.empty()) { - auto rebuild_transforms = !self.materials_buffer || self.transforms_buffer.data_size() <= info.gpu_transforms.size_bytes(); - self.transforms_buffer = self.transforms_buffer.resize(*self.device, info.gpu_transforms.size_bytes()).value(); + auto rebuild_transforms = !self.transforms_buffer || self.transforms_buffer.data_size() <= info.gpu_transforms.size_bytes(); + self.transforms_buffer = self.transforms_buffer.resize(device, info.gpu_transforms.size_bytes()).value(); if (rebuild_transforms) { // If we resize buffer, we need to refill it again, so individual uploads are not required. @@ -294,16 +295,16 @@ auto SceneRenderer::prepare_frame(this SceneRenderer &self, FramePrepareInfo &in } ); - prepared_frame.transforms_buffer = self.transforms_buffer.acquire(*self.device, "transforms", vuk::Access::eMemoryRead); + prepared_frame.transforms_buffer = self.transforms_buffer.acquire(device, "transforms", vuk::Access::eMemoryRead); prepared_frame.transforms_buffer = update_transforms_pass(std::move(upload_buffer), std::move(prepared_frame.transforms_buffer)); } } else if (self.transforms_buffer) { - prepared_frame.transforms_buffer = self.transforms_buffer.acquire(*self.device, "transforms", vuk::Access::eMemoryRead); + prepared_frame.transforms_buffer = self.transforms_buffer.acquire(device, "transforms", vuk::Access::eMemoryRead); } if (!info.dirty_material_indices.empty()) { auto rebuild_materials = !self.materials_buffer || self.materials_buffer.data_size() <= info.gpu_materials.size_bytes(); - self.materials_buffer = self.materials_buffer.resize(*self.device, info.gpu_materials.size_bytes()).value(); + self.materials_buffer = self.materials_buffer.resize(device, info.gpu_materials.size_bytes()).value(); if (rebuild_materials) { prepared_frame.materials_buffer = transfer_man.upload_staging(info.gpu_materials, self.materials_buffer); @@ -339,25 +340,25 @@ auto SceneRenderer::prepare_frame(this SceneRenderer &self, FramePrepareInfo &in } ); - prepared_frame.materials_buffer = self.materials_buffer.acquire(*self.device, "materials", vuk::eMemoryRead); + prepared_frame.materials_buffer = self.materials_buffer.acquire(device, "materials", vuk::eMemoryRead); prepared_frame.materials_buffer = update_materials_pass(std::move(upload_buffer), std::move(prepared_frame.materials_buffer)); } } else if (self.materials_buffer) { - prepared_frame.materials_buffer = self.materials_buffer.acquire(*self.device, "materials", vuk::eMemoryRead); + prepared_frame.materials_buffer = self.materials_buffer.acquire(device, "materials", vuk::eMemoryRead); } if (!info.gpu_meshes.empty()) { - self.meshes_buffer = self.meshes_buffer.resize(*self.device, info.gpu_meshes.size_bytes()).value(); + self.meshes_buffer = self.meshes_buffer.resize(device, info.gpu_meshes.size_bytes()).value(); prepared_frame.meshes_buffer = transfer_man.upload_staging(info.gpu_meshes, self.meshes_buffer); } else if (self.meshes_buffer) { - prepared_frame.meshes_buffer = self.meshes_buffer.acquire(*self.device, "meshes", vuk::eMemoryRead); + prepared_frame.meshes_buffer = self.meshes_buffer.acquire(device, "meshes", vuk::eMemoryRead); } if (!info.gpu_mesh_instances.empty()) { - self.mesh_instances_buffer = self.mesh_instances_buffer.resize(*self.device, info.gpu_mesh_instances.size_bytes()).value(); + self.mesh_instances_buffer = self.mesh_instances_buffer.resize(device, info.gpu_mesh_instances.size_bytes()).value(); prepared_frame.mesh_instances_buffer = transfer_man.upload_staging(info.gpu_mesh_instances, self.mesh_instances_buffer); } else if (self.mesh_instances_buffer) { - prepared_frame.mesh_instances_buffer = self.mesh_instances_buffer.acquire(*self.device, "mesh instances", vuk::eMemoryRead); + prepared_frame.mesh_instances_buffer = self.mesh_instances_buffer.acquire(device, "mesh instances", vuk::eMemoryRead); } if (info.max_meshlet_instance_count > 0) { @@ -371,37 +372,36 @@ auto SceneRenderer::prepare_frame(this SceneRenderer &self, FramePrepareInfo &in ); } + info.environment.transmittance_lut_size = self.sky_transmittance_lut_view.extent(); + info.environment.sky_view_lut_size = self.sky_view_lut_extent; + info.environment.multiscattering_lut_size = self.sky_multiscatter_lut_view.extent(); + info.environment.aerial_perspective_lut_size = self.sky_aerial_perspective_lut_extent; + prepared_frame.environment_buffer = transfer_man.scratch_buffer(info.environment); + prepared_frame.camera_buffer = transfer_man.scratch_buffer(info.camera); + prepared_frame.mesh_instance_count = info.mesh_instance_count; + prepared_frame.environment_flags = static_cast(info.environment.flags); return prepared_frame; } -auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, PreparedFrame &frame) -> vuk::Value { +auto SceneRenderer::render(this SceneRenderer &self, vuk::Value &&dst_attachment, SceneRenderInfo &info, PreparedFrame &frame) + -> vuk::Value { ZoneScoped; - auto &transfer_man = self.device->transfer_man(); - auto &bindless_descriptor_set = self.device->get_descriptor_set(); + auto &device = App::mod(); + auto &transfer_man = device.transfer_man(); + auto &bindless_descriptor_set = device.get_descriptor_set(); // ────────────────────────────────────────────────────────────────────── auto final_attachment = vuk::declare_ia( "final", { .usage = vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eColorAttachment, - .extent = info.extent, .format = vuk::Format::eB10G11R11UfloatPack32, - .sample_count = vuk::Samples::e1, - .level_count = 1, - .layer_count = 1 } - ); - final_attachment = vuk::clear_image(std::move(final_attachment), vuk::Black); - - auto result_attachment = vuk::declare_ia( - "result", - { .usage = vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eColorAttachment, - .format = info.format, .sample_count = vuk::Samples::e1 } ); - result_attachment.same_shape_as(final_attachment); - result_attachment = vuk::clear_image(std::move(result_attachment), vuk::Black); + final_attachment.same_shape_as(dst_attachment); + final_attachment = vuk::clear_image(std::move(final_attachment), vuk::Black); auto depth_attachment = vuk::declare_ia( "depth", @@ -413,25 +413,24 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep depth_attachment = vuk::clear_image(std::move(depth_attachment), vuk::DepthZero); auto sky_transmittance_lut_attachment = - self.sky_transmittance_lut_view - .acquire(*self.device, "sky transmittance lut", vuk::ImageUsageFlagBits::eSampled, vuk::Access::eComputeSampled); + self.sky_transmittance_lut_view.acquire(device, "sky transmittance lut", vuk::ImageUsageFlagBits::eSampled, vuk::Access::eComputeSampled); auto sky_multiscatter_lut_attachment = - self.sky_multiscatter_lut_view.acquire(*self.device, "sky multiscatter lut", vuk::ImageUsageFlagBits::eSampled, vuk::Access::eComputeSampled); + self.sky_multiscatter_lut_view.acquire(device, "sky multiscatter lut", vuk::ImageUsageFlagBits::eSampled, vuk::Access::eComputeSampled); auto hiz_extent = vuk::Extent3D{ - .width = (info.extent.width + 63_u32) & ~63_u32, - .height = (info.extent.height + 63_u32) & ~63_u32, + .width = (dst_attachment->extent.width + 63_u32) & ~63_u32, + .height = (dst_attachment->extent.height + 63_u32) & ~63_u32, .depth = 1, }; auto hiz_attachment = vuk::Value{}; if (self.hiz.extent() != hiz_extent || !self.hiz) { if (self.hiz_view) { - self.device->destroy(self.hiz_view.id()); + device.destroy(self.hiz_view.id()); } if (self.hiz) { - self.device->destroy(self.hiz.id()); + device.destroy(self.hiz.id()); } auto hiz_info = ImageInfo{ @@ -442,7 +441,7 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep .mip_count = std::bit_width(ls::max(hiz_extent.width, hiz_extent.height)) - 1_u32, .name = "HiZ", }; - self.hiz = Image::create(*self.device, hiz_info).value(); + self.hiz = Image::create(device, hiz_info).value(); auto hiz_view_info = ImageViewInfo{ .image_usage = vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eStorage, @@ -450,14 +449,12 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep .subresource_range = { .aspectMask = vuk::ImageAspectFlagBits::eColor, .levelCount = hiz_info.mip_count }, .name = "HiZ View", }; - self.hiz_view = ImageView::create(*self.device, self.hiz, hiz_view_info).value(); + self.hiz_view = ImageView::create(device, self.hiz, hiz_view_info).value(); - hiz_attachment = - self.hiz_view.acquire(*self.device, "HiZ", vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eStorage, vuk::eNone); + hiz_attachment = self.hiz_view.acquire(device, "HiZ", vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eStorage, vuk::eNone); hiz_attachment = vuk::clear_image(std::move(hiz_attachment), vuk::DepthZero); } else { - hiz_attachment = - self.hiz_view.acquire(*self.device, "HiZ", vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eStorage, vuk::eComputeRW); + hiz_attachment = self.hiz_view.acquire(device, "HiZ", vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eStorage, vuk::eComputeRW); } static constexpr auto sampler_min_clamp_reduction_mode = VkSamplerReductionModeCreateInfo{ @@ -503,25 +500,8 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep debug_drawer_buffer = transfer_man.scratch_buffer({}); } - auto sun_buffer = vuk::Value{}; - if (info.sun.has_value()) { - sun_buffer = transfer_man.scratch_buffer(info.sun.value()); - } - - auto atmosphere_buffer = vuk::Value{}; - if (info.atmosphere.has_value()) { - auto atmosphere = info.atmosphere.value(); - atmosphere.sky_view_lut_size = self.sky_view_lut_extent; - atmosphere.aerial_perspective_lut_size = self.sky_aerial_perspective_lut_extent; - atmosphere.transmittance_lut_size = self.sky_transmittance_lut_view.extent(); - atmosphere.multiscattering_lut_size = self.sky_multiscatter_lut_view.extent(); - atmosphere_buffer = transfer_man.scratch_buffer(atmosphere); - } - - auto camera_buffer = vuk::Value{}; - if (info.camera.has_value()) { - camera_buffer = transfer_man.scratch_buffer(info.camera.value()); - } + auto environment_buffer = std::move(frame.environment_buffer); + auto camera_buffer = std::move(frame.camera_buffer); if (frame.mesh_instance_count) { auto transforms_buffer = std::move(frame.transforms_buffer); @@ -743,11 +723,9 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep // ── VISBUFFER CLEAR ───────────────────────────────────────────────── auto vis_clear_pass = vuk::make_pass( "vis clear", - []( // - vuk::CommandBuffer &cmd_list, - VUK_IA(vuk::eComputeWrite) visbuffer, - VUK_IA(vuk::eComputeWrite) overdraw - ) { + [](vuk::CommandBuffer &cmd_list, // + VUK_IA(vuk::eComputeWrite) visbuffer, + VUK_IA(vuk::eComputeWrite) overdraw) { cmd_list // .bind_compute_pipeline("passes.visbuffer_clear") .bind_image(0, 0, visbuffer) @@ -877,7 +855,7 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep auto picked_texel = editor_mousepick_pass(visbuffer_attachment, meshlet_instances_buffer, mesh_instances_buffer, picking_texel_buffer); vuk::Compiler temp_compiler; - picked_texel.wait(self.device->get_allocator(), temp_compiler); + picked_texel.wait(device.get_allocator(), temp_compiler); u32 texel_data = 0; std::memcpy(&texel_data, picked_texel->mapped_ptr, sizeof(u32)); @@ -886,11 +864,9 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep auto hiz_generate_pass = vuk::make_pass( "hiz generate", - []( // - vuk::CommandBuffer &cmd_list, - VUK_IA(vuk::eComputeSampled) src, - VUK_IA(vuk::eComputeRW) dst - ) { + [](vuk::CommandBuffer &cmd_list, // + VUK_IA(vuk::eComputeSampled) src, + VUK_IA(vuk::eComputeRW) dst) { auto extent = dst->extent; auto mip_count = dst->level_count; LS_EXPECT(mip_count < 13); @@ -1036,90 +1012,82 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep std::move(metallic_roughness_occlusion_attachment) ); - if (info.atmosphere.has_value()) { - // ── BRDF ──────────────────────────────────────────────────────────── - auto brdf_pass = vuk::make_pass( - "brdf", - []( // - vuk::CommandBuffer &cmd_list, - VUK_IA(vuk::eColorWrite) dst, - VUK_BA(vuk::eFragmentRead) atmosphere, - VUK_BA(vuk::eFragmentRead) sun, - VUK_BA(vuk::eFragmentRead) camera, - VUK_IA(vuk::eFragmentSampled) sky_transmittance_lut, - VUK_IA(vuk::eFragmentSampled) sky_multiscatter_lut, - VUK_IA(vuk::eFragmentSampled) depth, - VUK_IA(vuk::eFragmentSampled) albedo, - VUK_IA(vuk::eFragmentSampled) normal, - VUK_IA(vuk::eFragmentSampled) emissive, - VUK_IA(vuk::eFragmentSampled) metallic_roughness_occlusion - ) { - auto linear_clamp_sampler = vuk::SamplerCreateInfo{ - .magFilter = vuk::Filter::eLinear, - .minFilter = vuk::Filter::eLinear, - .addressModeU = vuk::SamplerAddressMode::eClampToEdge, - .addressModeV = vuk::SamplerAddressMode::eClampToEdge, - .addressModeW = vuk::SamplerAddressMode::eClampToEdge, - }; - - auto linear_repeat_sampler = vuk::SamplerCreateInfo{ - .magFilter = vuk::Filter::eLinear, - .minFilter = vuk::Filter::eLinear, - .addressModeU = vuk::SamplerAddressMode::eRepeat, - .addressModeV = vuk::SamplerAddressMode::eRepeat, - }; + // ── BRDF ──────────────────────────────────────────────────────────── + auto brdf_pass = vuk::make_pass( + "brdf", + [](vuk::CommandBuffer &cmd_list, // + VUK_IA(vuk::eColorWrite) dst, + VUK_BA(vuk::eFragmentRead) environment, + VUK_BA(vuk::eFragmentRead) camera, + VUK_IA(vuk::eFragmentSampled) sky_transmittance_lut, + VUK_IA(vuk::eFragmentSampled) sky_multiscatter_lut, + VUK_IA(vuk::eFragmentSampled) depth, + VUK_IA(vuk::eFragmentSampled) albedo, + VUK_IA(vuk::eFragmentSampled) normal, + VUK_IA(vuk::eFragmentSampled) emissive, + VUK_IA(vuk::eFragmentSampled) metallic_roughness_occlusion) { + auto linear_clamp_sampler = vuk::SamplerCreateInfo{ + .magFilter = vuk::Filter::eLinear, + .minFilter = vuk::Filter::eLinear, + .addressModeU = vuk::SamplerAddressMode::eClampToEdge, + .addressModeV = vuk::SamplerAddressMode::eClampToEdge, + .addressModeW = vuk::SamplerAddressMode::eClampToEdge, + }; - cmd_list // - .bind_graphics_pipeline("passes.brdf") - .set_rasterization({}) - .set_color_blend(dst, vuk::BlendPreset::eOff) - .set_dynamic_state(vuk::DynamicStateFlagBits::eViewport | vuk::DynamicStateFlagBits::eScissor) - .set_viewport(0, vuk::Rect2D::framebuffer()) - .set_scissor(0, vuk::Rect2D::framebuffer()) - .bind_sampler(0, 0, linear_clamp_sampler) - .bind_sampler(0, 1, linear_repeat_sampler) - .bind_image(0, 2, sky_transmittance_lut) - .bind_image(0, 3, sky_multiscatter_lut) - .bind_image(0, 4, depth) - .bind_image(0, 5, albedo) - .bind_image(0, 6, normal) - .bind_image(0, 7, emissive) - .bind_image(0, 8, metallic_roughness_occlusion) - .bind_buffer(0, 9, atmosphere) - .bind_buffer(0, 10, sun) - .bind_buffer(0, 11, camera) - .draw(3, 1, 0, 0); - - return std::make_tuple(dst, atmosphere, sun, camera, sky_transmittance_lut, sky_multiscatter_lut, depth); - } - ); + auto linear_repeat_sampler = vuk::SamplerCreateInfo{ + .magFilter = vuk::Filter::eLinear, + .minFilter = vuk::Filter::eLinear, + .addressModeU = vuk::SamplerAddressMode::eRepeat, + .addressModeV = vuk::SamplerAddressMode::eRepeat, + }; - std::tie( - final_attachment, - atmosphere_buffer, - sun_buffer, - camera_buffer, - sky_transmittance_lut_attachment, - sky_multiscatter_lut_attachment, - depth_attachment - ) = - brdf_pass( - std::move(final_attachment), - std::move(atmosphere_buffer), - std::move(sun_buffer), - std::move(camera_buffer), - std::move(sky_transmittance_lut_attachment), - std::move(sky_multiscatter_lut_attachment), - std::move(depth_attachment), - std::move(albedo_attachment), - std::move(normal_attachment), - std::move(emissive_attachment), - std::move(metallic_roughness_occlusion_attachment) - ); - } + cmd_list // + .bind_graphics_pipeline("passes.brdf") + .set_rasterization({}) + .set_color_blend(dst, vuk::BlendPreset::eOff) + .set_dynamic_state(vuk::DynamicStateFlagBits::eViewport | vuk::DynamicStateFlagBits::eScissor) + .set_viewport(0, vuk::Rect2D::framebuffer()) + .set_scissor(0, vuk::Rect2D::framebuffer()) + .bind_sampler(0, 0, linear_clamp_sampler) + .bind_sampler(0, 1, linear_repeat_sampler) + .bind_image(0, 2, sky_transmittance_lut) + .bind_image(0, 3, sky_multiscatter_lut) + .bind_image(0, 4, depth) + .bind_image(0, 5, albedo) + .bind_image(0, 6, normal) + .bind_image(0, 7, emissive) + .bind_image(0, 8, metallic_roughness_occlusion) + .bind_buffer(0, 9, environment) + .bind_buffer(0, 10, camera) + .draw(3, 1, 0, 0); + + return std::make_tuple(dst, environment, camera, sky_transmittance_lut, sky_multiscatter_lut, depth); + } + ); + + std::tie( + final_attachment, + environment_buffer, + camera_buffer, + sky_transmittance_lut_attachment, + sky_multiscatter_lut_attachment, + depth_attachment + ) = + brdf_pass( + std::move(final_attachment), + std::move(environment_buffer), + std::move(camera_buffer), + std::move(sky_transmittance_lut_attachment), + std::move(sky_multiscatter_lut_attachment), + std::move(depth_attachment), + std::move(albedo_attachment), + std::move(normal_attachment), + std::move(emissive_attachment), + std::move(metallic_roughness_occlusion_attachment) + ); } - if (info.atmosphere.has_value()) { + if (frame.environment_flags & (GPU::EnvironmentFlags::HasSun | GPU::EnvironmentFlags::HasAtmosphere)) { auto sky_view_lut_attachment = vuk::declare_ia( "sky view lut", { .image_type = vuk::ImageType::e2D, @@ -1147,15 +1115,12 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep // ── SKY VIEW LUT ──────────────────────────────────────────────────── auto sky_view_pass = vuk::make_pass( "sky view", - []( // - vuk::CommandBuffer &cmd_list, - VUK_IA(vuk::eComputeSampled) sky_transmittance_lut, - VUK_IA(vuk::eComputeSampled) sky_multiscatter_lut, - VUK_BA(vuk::eComputeRead) atmosphere, - VUK_BA(vuk::eComputeRead) sun, - VUK_BA(vuk::eComputeRead) camera, - VUK_IA(vuk::eComputeRW) sky_view_lut - ) { + [](vuk::CommandBuffer &cmd_list, // + VUK_IA(vuk::eComputeSampled) sky_transmittance_lut, + VUK_IA(vuk::eComputeSampled) sky_multiscatter_lut, + VUK_BA(vuk::eComputeRead) environment, + VUK_BA(vuk::eComputeRead) camera, + VUK_IA(vuk::eComputeRW) sky_view_lut) { auto linear_clamp_sampler = vuk::SamplerCreateInfo{ .magFilter = vuk::Filter::eLinear, .minFilter = vuk::Filter::eLinear, @@ -1169,27 +1134,18 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep .bind_sampler(0, 0, linear_clamp_sampler) .bind_image(0, 1, sky_transmittance_lut) .bind_image(0, 2, sky_multiscatter_lut) - .bind_buffer(0, 3, atmosphere) - .bind_buffer(0, 4, sun) - .bind_buffer(0, 5, camera) - .bind_image(0, 6, sky_view_lut) + .bind_buffer(0, 3, environment) + .bind_buffer(0, 4, camera) + .bind_image(0, 5, sky_view_lut) .dispatch_invocations_per_pixel(sky_view_lut); - return std::make_tuple(sky_transmittance_lut, sky_multiscatter_lut, atmosphere, sun, camera, sky_view_lut); + return std::make_tuple(sky_transmittance_lut, sky_multiscatter_lut, environment, camera, sky_view_lut); } ); - std::tie( - sky_transmittance_lut_attachment, - sky_multiscatter_lut_attachment, - atmosphere_buffer, - sun_buffer, - camera_buffer, - sky_view_lut_attachment - ) = + std::tie(sky_transmittance_lut_attachment, sky_multiscatter_lut_attachment, environment_buffer, camera_buffer, sky_view_lut_attachment) = sky_view_pass( std::move(sky_transmittance_lut_attachment), std::move(sky_multiscatter_lut_attachment), - std::move(atmosphere_buffer), - std::move(sun_buffer), + std::move(environment_buffer), std::move(camera_buffer), std::move(sky_view_lut_attachment) ); @@ -1197,15 +1153,12 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep // ── SKY AERIAL PERSPECTIVE ────────────────────────────────────────── auto sky_aerial_perspective_pass = vuk::make_pass( "sky aerial perspective", - []( // - vuk::CommandBuffer &cmd_list, - VUK_IA(vuk::eComputeSampled) sky_transmittance_lut, - VUK_IA(vuk::eComputeSampled) sky_multiscatter_lut, - VUK_BA(vuk::eComputeRead) atmosphere, - VUK_BA(vuk::eComputeRead) sun, - VUK_BA(vuk::eComputeRead) camera, - VUK_IA(vuk::eComputeRW) sky_aerial_perspective_lut - ) { + [](vuk::CommandBuffer &cmd_list, // + VUK_IA(vuk::eComputeSampled) sky_transmittance_lut, + VUK_IA(vuk::eComputeSampled) sky_multiscatter_lut, + VUK_BA(vuk::eComputeRead) environment, + VUK_BA(vuk::eComputeRead) camera, + VUK_IA(vuk::eComputeRW) sky_aerial_perspective_lut) { auto linear_clamp_sampler = vuk::SamplerCreateInfo{ .magFilter = vuk::Filter::eLinear, .minFilter = vuk::Filter::eLinear, @@ -1219,28 +1172,25 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep .bind_sampler(0, 0, linear_clamp_sampler) .bind_image(0, 1, sky_transmittance_lut) .bind_image(0, 2, sky_multiscatter_lut) - .bind_buffer(0, 3, atmosphere) - .bind_buffer(0, 4, sun) - .bind_buffer(0, 5, camera) - .bind_image(0, 6, sky_aerial_perspective_lut) + .bind_buffer(0, 3, environment) + .bind_buffer(0, 4, camera) + .bind_image(0, 5, sky_aerial_perspective_lut) .dispatch_invocations_per_pixel(sky_aerial_perspective_lut); - return std::make_tuple(sky_transmittance_lut, sky_multiscatter_lut, atmosphere, sun, camera, sky_aerial_perspective_lut); + return std::make_tuple(sky_transmittance_lut, sky_multiscatter_lut, environment, camera, sky_aerial_perspective_lut); } ); std::tie( sky_transmittance_lut_attachment, sky_multiscatter_lut_attachment, - atmosphere_buffer, - sun_buffer, + environment_buffer, camera_buffer, sky_aerial_perspective_attachment ) = sky_aerial_perspective_pass( std::move(sky_transmittance_lut_attachment), std::move(sky_multiscatter_lut_attachment), - std::move(atmosphere_buffer), - std::move(sun_buffer), + std::move(environment_buffer), std::move(camera_buffer), std::move(sky_aerial_perspective_attachment) ); @@ -1248,17 +1198,14 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep // ── SKY FINAL ─────────────────────────────────────────────────────── auto sky_final_pass = vuk::make_pass( "sky final", - []( // - vuk::CommandBuffer &cmd_list, - VUK_IA(vuk::eColorWrite) dst, - VUK_IA(vuk::eFragmentSampled) sky_transmittance_lut, - VUK_IA(vuk::eFragmentSampled) sky_aerial_perspective_lut, - VUK_IA(vuk::eFragmentSampled) sky_view_lut, - VUK_IA(vuk::eFragmentSampled) depth, - VUK_BA(vuk::eFragmentRead) atmosphere, - VUK_BA(vuk::eFragmentRead) sun, - VUK_BA(vuk::eFragmentRead) camera - ) { + [](vuk::CommandBuffer &cmd_list, // + VUK_IA(vuk::eColorWrite) dst, + VUK_IA(vuk::eFragmentSampled) sky_transmittance_lut, + VUK_IA(vuk::eFragmentSampled) sky_aerial_perspective_lut, + VUK_IA(vuk::eFragmentSampled) sky_view_lut, + VUK_IA(vuk::eFragmentSampled) depth, + VUK_BA(vuk::eFragmentRead) environment, + VUK_BA(vuk::eFragmentRead) camera) { auto linear_clamp_sampler = vuk::SamplerCreateInfo{ .magFilter = vuk::Filter::eLinear, .minFilter = vuk::Filter::eLinear, @@ -1290,100 +1237,83 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep .bind_image(0, 2, sky_aerial_perspective_lut) .bind_image(0, 3, sky_view_lut) .bind_image(0, 4, depth) - .bind_buffer(0, 5, atmosphere) - .bind_buffer(0, 6, sun) - .bind_buffer(0, 7, camera) + .bind_buffer(0, 5, environment) + .bind_buffer(0, 6, camera) .draw(3, 1, 0, 0); - return std::make_tuple(dst, depth, camera); + + return std::make_tuple(dst, depth, environment, camera); } ); - std::tie(final_attachment, depth_attachment, camera_buffer) = sky_final_pass( + std::tie(final_attachment, depth_attachment, environment_buffer, camera_buffer) = sky_final_pass( std::move(final_attachment), std::move(sky_transmittance_lut_attachment), std::move(sky_aerial_perspective_attachment), std::move(sky_view_lut_attachment), std::move(depth_attachment), - std::move(atmosphere_buffer), - std::move(sun_buffer), + std::move(environment_buffer), std::move(camera_buffer) ); } - auto histogram_info = info.histogram_info.value_or(GPU::HistogramInfo{}); - - // ── HISTOGRAM GENERATE ────────────────────────────────────────────── - auto histogram_generate_pass = vuk::make_pass( + auto histogram_luminance_buffer = self.histogram_luminance_buffer.acquire(device, "histogram luminance", vuk::eNone); + if (frame.environment_flags & GPU::EnvironmentFlags::HasEyeAdaptation) { + // ── HISTOGRAM GENERATE ────────────────────────────────────────────── + auto histogram_generate_pass = vuk::make_pass( "histogram generate", - [histogram_info]( // - vuk::CommandBuffer &cmd_list, - VUK_IA(vuk::eComputeRead) src, - VUK_BA(vuk::eComputeRW) histogram - ) { + [](vuk::CommandBuffer &cmd_list, // + VUK_IA(vuk::eComputeRead) src_image, + VUK_BA(vuk::eComputeRead) environment, + VUK_BA(vuk::eComputeRW) histogram_bin_indices) { cmd_list.bind_compute_pipeline("passes.histogram_generate") - .bind_image(0, 0, src) - .push_constants( - vuk::ShaderStageFlagBits::eCompute, - 0, - PushConstants( // - histogram->device_address, - glm::uvec2(src->extent.width, src->extent.height), - histogram_info.min_exposure, - 1.0f / (histogram_info.max_exposure - histogram_info.min_exposure) - ) - ) - .dispatch_invocations_per_pixel(src); + .bind_image(0, 0, src_image) + .bind_buffer(0, 1, environment) + .bind_buffer(0, 2, histogram_bin_indices) + .push_constants(vuk::ShaderStageFlagBits::eCompute, 0, src_image->extent) + .dispatch_invocations_per_pixel(src_image); - return std::make_tuple(src, histogram); + return std::make_tuple(src_image, environment, histogram_bin_indices); } ); - auto histogram_buffer = transfer_man.alloc_transient_buffer(vuk::MemoryUsage::eGPUonly, GPU::HISTOGRAM_BIN_COUNT * sizeof(u32)); - vuk::fill(histogram_buffer, 0); - std::tie(final_attachment, histogram_buffer) = histogram_generate_pass(std::move(final_attachment), std::move(histogram_buffer)); + auto histogram_bin_indices_buffer = transfer_man.alloc_transient_buffer(vuk::MemoryUsage::eGPUonly, GPU::HISTOGRAM_BIN_COUNT * sizeof(u32)); + vuk::fill(histogram_bin_indices_buffer, 0); + std::tie(final_attachment, environment_buffer, histogram_bin_indices_buffer) = + histogram_generate_pass(std::move(final_attachment), std::move(environment_buffer), std::move(histogram_bin_indices_buffer)); - // ── HISTOGRAM AVERAGE ─────────────────────────────────────────────── - auto histogram_average_pass = vuk::make_pass( + // ── HISTOGRAM AVERAGE ─────────────────────────────────────────────── + auto histogram_average_pass = vuk::make_pass( "histogram average", - [pixel_count = f32(final_attachment->extent.width * final_attachment->extent.height), delta_time = info.delta_time, histogram_info]( // - vuk::CommandBuffer &cmd_list, - VUK_BA(vuk::eComputeRW) histogram, - VUK_BA(vuk::eComputeRW) exposure + [pixel_count = f32(dst_attachment->extent.width * dst_attachment->extent.height), delta_time = info.delta_time]( + vuk::CommandBuffer &cmd_list, // + VUK_BA(vuk::eComputeRead) environment, + VUK_BA(vuk::eComputeRead) histogram_bin_indices, + VUK_BA(vuk::eComputeRW) histogram_luminance ) { - cmd_list.bind_compute_pipeline("passes.histogram_average") - .push_constants( - vuk::ShaderStageFlagBits::eCompute, - 0, - PushConstants( // - histogram->device_address, - exposure->device_address, - pixel_count, - histogram_info.min_exposure, - histogram_info.max_exposure - histogram_info.min_exposure, - glm::clamp(static_cast(1.0f - glm::exp(-histogram_info.adaptation_speed * delta_time)), 0.0f, 1.0f), - histogram_info.ISO_K - ) - ) + cmd_list // + .bind_compute_pipeline("passes.histogram_average") + .bind_buffer(0, 0, environment) + .bind_buffer(0, 1, histogram_bin_indices) + .bind_buffer(0, 2, histogram_luminance) + .push_constants(vuk::ShaderStageFlagBits::eCompute, 0, PushConstants(pixel_count, delta_time)) .dispatch(1); - return exposure; + return std::make_tuple(environment, histogram_luminance); } ); - auto exposure_buffer = self.exposure_buffer.acquire(*self.device, "exposure", vuk::eNone); - if (info.histogram_info.has_value()) { - exposure_buffer = histogram_average_pass(std::move(histogram_buffer), std::move(exposure_buffer)); + std::tie(environment_buffer, histogram_luminance_buffer) = + histogram_average_pass(std::move(environment_buffer), std::move(histogram_bin_indices_buffer), std::move(histogram_luminance_buffer)); } // ── TONEMAP ───────────────────────────────────────────────────────── auto tonemap_pass = vuk::make_pass( "tonemap", - []( // - vuk::CommandBuffer &cmd_list, - VUK_IA(vuk::eColorWrite) dst, - VUK_IA(vuk::eFragmentSampled) src, - VUK_BA(vuk::eFragmentRead) exposure - ) { + [](vuk::CommandBuffer &cmd_list, // + VUK_IA(vuk::eColorWrite) dst, + VUK_IA(vuk::eFragmentSampled) src, + VUK_BA(vuk::eFragmentRead) environment, + VUK_BA(vuk::eFragmentRead) histogram_luminance) { cmd_list // .bind_graphics_pipeline("passes.tonemap") .set_rasterization({}) @@ -1393,12 +1323,15 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep .set_scissor(0, vuk::Rect2D::framebuffer()) .bind_sampler(0, 0, { .magFilter = vuk::Filter::eLinear, .minFilter = vuk::Filter::eLinear }) .bind_image(0, 1, src) - .push_constants(vuk::ShaderStageFlagBits::eFragment, 0, exposure->device_address) + .bind_buffer(0, 2, environment) + .bind_buffer(0, 3, histogram_luminance) .draw(3, 1, 0, 0); + return dst; } ); - result_attachment = tonemap_pass(std::move(result_attachment), std::move(final_attachment), std::move(exposure_buffer)); + dst_attachment = + tonemap_pass(std::move(dst_attachment), std::move(final_attachment), std::move(environment_buffer), std::move(histogram_luminance_buffer)); // ── EDITOR GRID ───────────────────────────────────────────────────── auto editor_grid_pass = vuk::make_pass( @@ -1459,8 +1392,8 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep } ); - result_attachment = debug_pass( - std::move(result_attachment), + dst_attachment = debug_pass( + std::move(dst_attachment), std::move(debug_drawer_buffer), std::move(camera_buffer), std::move(debug_draw_aabb_buffer), @@ -1468,41 +1401,43 @@ auto SceneRenderer::render(this SceneRenderer &self, SceneRenderInfo &info, Prep ); } - return result_attachment; + return dst_attachment; } auto SceneRenderer::cleanup(this SceneRenderer &self) -> void { ZoneScoped; - self.device->wait(); + auto &device = App::mod(); + + device.wait(); if (self.transforms_buffer) { - self.device->destroy(self.transforms_buffer.id()); + device.destroy(self.transforms_buffer.id()); self.transforms_buffer = {}; } if (self.mesh_instances_buffer) { - self.device->destroy(self.mesh_instances_buffer.id()); + device.destroy(self.mesh_instances_buffer.id()); self.mesh_instances_buffer = {}; } if (self.meshes_buffer) { - self.device->destroy(self.meshes_buffer.id()); + device.destroy(self.meshes_buffer.id()); self.meshes_buffer = {}; } if (self.materials_buffer) { - self.device->destroy(self.materials_buffer.id()); + device.destroy(self.materials_buffer.id()); self.materials_buffer = {}; } if (self.hiz_view) { - self.device->destroy(self.hiz_view.id()); + device.destroy(self.hiz_view.id()); self.hiz_view = {}; } if (self.hiz) { - self.device->destroy(self.hiz.id()); + device.destroy(self.hiz.id()); self.hiz = {}; } } diff --git a/Lorr/Engine/Scene/SceneRenderer.hh b/Lorr/Engine/Scene/SceneRenderer.hh index 229cf86d..eae109e9 100644 --- a/Lorr/Engine/Scene/SceneRenderer.hh +++ b/Lorr/Engine/Scene/SceneRenderer.hh @@ -17,10 +17,14 @@ struct FramePrepareInfo { ls::span gpu_meshes = {}; ls::span gpu_mesh_instances = {}; + + GPU::Environment environment = {}; + GPU::Camera camera = {}; }; struct PreparedFrame { u32 mesh_instance_count = 0; + GPU::EnvironmentFlags environment_flags = GPU::EnvironmentFlags::None; vuk::Value transforms_buffer = {}; vuk::Value meshes_buffer = {}; vuk::Value mesh_instances_buffer = {}; @@ -28,28 +32,23 @@ struct PreparedFrame { vuk::Value visible_meshlet_instances_indices_buffer = {}; vuk::Value reordered_indices_buffer = {}; vuk::Value materials_buffer = {}; + vuk::Value environment_buffer = {}; + vuk::Value camera_buffer = {}; }; struct SceneRenderInfo { - vuk::Format format = vuk::Format::eR8G8B8A8Srgb; - vuk::Extent3D extent = {}; f32 delta_time = 0.0f; GPU::CullFlags cull_flags = {}; - ls::option sun = ls::nullopt; - ls::option atmosphere = ls::nullopt; - ls::option camera = ls::nullopt; ls::option picking_texel = ls::nullopt; - ls::option histogram_info = ls::nullopt; - ls::option picked_transform_index = ls::nullopt; }; struct SceneRenderer { - Device *device = nullptr; + static constexpr auto MODULE_NAME = "Scene Renderer"; // Scene resources - Buffer exposure_buffer = {}; + Buffer histogram_luminance_buffer = {}; Buffer transforms_buffer = {}; Buffer mesh_instances_buffer = {}; @@ -71,14 +70,13 @@ struct SceneRenderer { bool debug_lines = false; - auto init(this SceneRenderer &, Device *device) -> bool; + auto init(this SceneRenderer &) -> bool; auto destroy(this SceneRenderer &) -> void; - auto create_persistent_resources(this SceneRenderer &) -> void; - // Scene auto prepare_frame(this SceneRenderer &, FramePrepareInfo &info) -> PreparedFrame; - auto render(this SceneRenderer &, SceneRenderInfo &render_info, PreparedFrame &frame) -> vuk::Value; + auto render(this SceneRenderer &, vuk::Value &&dst_attachment, SceneRenderInfo &render_info, PreparedFrame &frame) + -> vuk::Value; auto cleanup(this SceneRenderer &) -> void; }; diff --git a/Lorr/Engine/Window/Window.cc b/Lorr/Engine/Window/Window.cc index b97c0e9c..23da7148 100644 --- a/Lorr/Engine/Window/Window.cc +++ b/Lorr/Engine/Window/Window.cc @@ -1,5 +1,9 @@ #include "Engine/Window/Window.hh" +#include "Engine/Core/App.hh" + +#include "Engine/Graphics/VulkanDevice.hh" + #include "Engine/Memory/Stack.hh" #include @@ -8,84 +12,106 @@ #include namespace lr { -template<> -struct Handle::Impl { - u32 width = 0; - u32 height = 0; - - WindowCursor current_cursor = WindowCursor::Arrow; - glm::uvec2 cursor_position = {}; +auto Window::init_sdl() -> bool { + ZoneScoped; - SDL_Window *handle = nullptr; - u32 monitor_id = WindowInfo::USE_PRIMARY_MONITOR; - std::array cursors = {}; -}; + return SDL_Init(SDL_INIT_EVENTS | SDL_INIT_VIDEO); +} -auto Window::create(const WindowInfo &info) -> Window { +auto Window::display_at(i32 monitor_id) -> ls::option { ZoneScoped; - if (!SDL_Init(SDL_INIT_EVENTS | SDL_INIT_VIDEO)) { - LOG_ERROR("Failed to initialize SDL! {}", SDL_GetError()); - return Handle(nullptr); + i32 display_count = 0; + auto *display_ids = SDL_GetDisplays(&display_count); + LS_DEFER(&) { + SDL_free(display_ids); + }; + + if (display_count == 0 || display_ids == nullptr) { + return ls::nullopt; + } + + const auto checking_display = display_ids[monitor_id]; + const char *monitor_name = SDL_GetDisplayName(checking_display); + const auto *display_mode = SDL_GetDesktopDisplayMode(checking_display); + if (display_mode == nullptr) { + return ls::nullopt; + } + + SDL_Rect position_bounds = {}; + if (!SDL_GetDisplayBounds(checking_display, &position_bounds)) { + return ls::nullopt; } - auto display = Window::display_at(info.monitor); - if (!display.has_value()) { - LOG_ERROR("No available displays!"); - return Handle(nullptr); + SDL_Rect work_bounds = {}; + if (!SDL_GetDisplayUsableBounds(checking_display, &work_bounds)) { + return ls::nullopt; } + return SystemDisplay{ + .name = monitor_name, + .position = { position_bounds.x, position_bounds.y }, + .work_area = { work_bounds.x, work_bounds.y, work_bounds.w, work_bounds.h }, + .resolution = { display_mode->w, display_mode->h }, + .refresh_rate = display_mode->refresh_rate, + }; +} + +Window::Window(const WindowInfo &info) : title(info.title), width(info.width), height(info.height), flags(info.flags), display(info.display) { + ZoneScoped; +} + +auto Window::init(this Window &self) -> bool { + ZoneScoped; + i32 new_pos_x = SDL_WINDOWPOS_UNDEFINED; i32 new_pos_y = SDL_WINDOWPOS_UNDEFINED; - i32 new_width = static_cast(info.width); - i32 new_height = static_cast(info.height); - - if (info.flags & WindowFlag::WorkAreaRelative) { - new_pos_x = display->work_area.x; - new_pos_y = display->work_area.y; - new_width = display->work_area.z; - new_height = display->work_area.w; - } else if (info.flags & WindowFlag::Centered) { + + if (self.flags & WindowFlag::WorkAreaRelative) { + LS_EXPECT(self.display != nullptr); + + new_pos_x = self.display->work_area.x; + new_pos_y = self.display->work_area.y; + self.width = self.display->work_area.z; + self.height = self.display->work_area.w; + } else if (self.flags & WindowFlag::Centered) { new_pos_x = SDL_WINDOWPOS_CENTERED; new_pos_y = SDL_WINDOWPOS_CENTERED; } u32 window_flags = SDL_WINDOW_VULKAN; - if (info.flags & WindowFlag::Resizable) { + if (self.flags & WindowFlag::Resizable) { window_flags |= SDL_WINDOW_RESIZABLE; } - if (info.flags & WindowFlag::Borderless) { + if (self.flags & WindowFlag::Borderless) { window_flags |= SDL_WINDOW_BORDERLESS; } - if (info.flags & WindowFlag::Maximized) { + if (self.flags & WindowFlag::Maximized) { window_flags |= SDL_WINDOW_MAXIMIZED; } - if (info.flags & WindowFlag::Fullscreen) { + if (self.flags & WindowFlag::Fullscreen) { window_flags |= SDL_WINDOW_FULLSCREEN; } - auto impl = new Impl; - impl->width = static_cast(new_width); - impl->height = static_cast(new_height); - impl->monitor_id = info.monitor; - auto window_properties = SDL_CreateProperties(); LS_DEFER(&) { SDL_DestroyProperties(window_properties); }; - SDL_SetStringProperty(window_properties, SDL_PROP_WINDOW_CREATE_TITLE_STRING, info.title.c_str()); + SDL_SetStringProperty(window_properties, SDL_PROP_WINDOW_CREATE_TITLE_STRING, self.title.c_str()); SDL_SetNumberProperty(window_properties, SDL_PROP_WINDOW_CREATE_X_NUMBER, new_pos_x); SDL_SetNumberProperty(window_properties, SDL_PROP_WINDOW_CREATE_Y_NUMBER, new_pos_y); - SDL_SetNumberProperty(window_properties, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, new_width); - SDL_SetNumberProperty(window_properties, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, new_height); + SDL_SetNumberProperty(window_properties, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, self.width); + SDL_SetNumberProperty(window_properties, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, self.height); SDL_SetNumberProperty(window_properties, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, window_flags); - impl->handle = SDL_CreateWindowWithProperties(window_properties); + self.handle = SDL_CreateWindowWithProperties(window_properties); - impl->cursors = { + SDL_GetWindowSizeInPixels(self.handle, &self.width, &self.height); + SDL_StartTextInput(self.handle); + self.cursors = { SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT), SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT), SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE), SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NS_RESIZE), SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_EW_RESIZE), SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NESW_RESIZE), @@ -93,143 +119,76 @@ auto Window::create(const WindowInfo &info) -> Window { SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED), }; - i32 real_width; - i32 real_height; - SDL_GetWindowSizeInPixels(impl->handle, &real_width, &real_height); - SDL_StartTextInput(impl->handle); + self.set_cursor(WindowCursor::Arrow); - impl->width = real_width; - impl->height = real_height; + auto &device = App::mod(); + auto surface = self.get_surface(device.get_instance()); + self.swap_chain.emplace(device.create_swap_chain(surface).value()); - auto self = Window(impl); - self.set_cursor(WindowCursor::Arrow); - return self; + return true; } -auto Window::destroy() -> void { +auto Window::destroy(this Window &self) -> void { ZoneScoped; - SDL_StopTextInput(impl->handle); - SDL_DestroyWindow(impl->handle); + self.swap_chain.reset(); + SDL_StopTextInput(self.handle); + SDL_DestroyWindow(self.handle); } -auto Window::poll(const WindowCallbacks &callbacks) -> void { +auto Window::update(this Window &self, f64) -> void { ZoneScoped; SDL_Event e = {}; while (SDL_PollEvent(&e) != 0) { switch (e.type) { case SDL_EVENT_WINDOW_RESIZED: { - if (callbacks.on_resize) { - callbacks.on_resize(callbacks.user_data, { e.window.data1, e.window.data2 }); - } - } break; - case SDL_EVENT_MOUSE_MOTION: { - if (callbacks.on_mouse_pos) { - callbacks.on_mouse_pos(callbacks.user_data, { e.motion.x, e.motion.y }, { e.motion.xrel, e.motion.yrel }); - } - } break; - case SDL_EVENT_MOUSE_BUTTON_DOWN: - case SDL_EVENT_MOUSE_BUTTON_UP: { - if (callbacks.on_mouse_button) { - auto state = e.type == SDL_EVENT_MOUSE_BUTTON_DOWN; - callbacks.on_mouse_button(callbacks.user_data, e.button.button, state); - } - } break; - case SDL_EVENT_MOUSE_WHEEL: { - if (callbacks.on_mouse_scroll) { - callbacks.on_mouse_scroll(callbacks.user_data, { e.wheel.x, e.wheel.y }); - } - } break; - case SDL_EVENT_KEY_DOWN: - case SDL_EVENT_KEY_UP: { - if (callbacks.on_key) { - auto state = e.type == SDL_EVENT_KEY_DOWN; - callbacks.on_key(callbacks.user_data, e.key.key, e.key.scancode, e.key.mod, state); - } - } break; - case SDL_EVENT_TEXT_INPUT: { - if (callbacks.on_text_input) { - callbacks.on_text_input(callbacks.user_data, e.text.text); - } + auto &device = App::mod(); + + device.wait(); + auto surface = self.get_surface(device.get_instance()); + self.swap_chain = device.create_swap_chain(surface, std::move(self.swap_chain)).value(); } break; case SDL_EVENT_QUIT: { - if (callbacks.on_close) { - callbacks.on_close(callbacks.user_data); - } + App::close(); } break; default:; } + + for (const auto &cb : self.event_listeners) { + cb(e); + } } } -auto Window::set_cursor(WindowCursor cursor) -> void { +auto Window::set_cursor(this Window &self, WindowCursor cursor) -> void { ZoneScoped; - impl->current_cursor = cursor; - SDL_SetCursor(impl->cursors[usize(cursor)]); + self.current_cursor = cursor; + SDL_SetCursor(self.cursors[usize(cursor)]); } -auto Window::get_cursor() -> WindowCursor { +auto Window::get_cursor(this Window &self) -> WindowCursor { ZoneScoped; - return impl->current_cursor; + return self.current_cursor; } -auto Window::show_cursor(bool show) -> void { +auto Window::show_cursor(this Window &, bool show) -> void { ZoneScoped; show ? SDL_ShowCursor() : SDL_HideCursor(); } -auto Window::display_at(i32 monitor_id) -> ls::option { - ZoneScoped; - - i32 display_count = 0; - auto *display_ids = SDL_GetDisplays(&display_count); - LS_DEFER(&) { - SDL_free(display_ids); - }; - - if (display_count == 0 || display_ids == nullptr) { - return ls::nullopt; - } - - const auto checking_display = display_ids[monitor_id]; - const char *monitor_name = SDL_GetDisplayName(checking_display); - const auto *display_mode = SDL_GetDesktopDisplayMode(checking_display); - if (display_mode == nullptr) { - return ls::nullopt; - } - - SDL_Rect position_bounds = {}; - if (!SDL_GetDisplayBounds(checking_display, &position_bounds)) { - return ls::nullopt; - } - - SDL_Rect work_bounds = {}; - if (!SDL_GetDisplayUsableBounds(checking_display, &work_bounds)) { - return ls::nullopt; - } - - return SystemDisplay{ - .name = monitor_name, - .position = { position_bounds.x, position_bounds.y }, - .work_area = { work_bounds.x, work_bounds.y, work_bounds.w, work_bounds.h }, - .resolution = { display_mode->w, display_mode->h }, - .refresh_rate = display_mode->refresh_rate, - }; -} - -auto Window::get_size() -> glm::uvec2 { - return { impl->width, impl->height }; +auto Window::get_size(this Window &self) -> glm::ivec2 { + return { self.width, self.height }; } -auto Window::get_surface(VkInstance instance) -> VkSurfaceKHR { +auto Window::get_surface(this Window &self, VkInstance instance) -> VkSurfaceKHR { ZoneScoped; VkSurfaceKHR surface = {}; - if (!SDL_Vulkan_CreateSurface(impl->handle, instance, nullptr, &surface)) { + if (!SDL_Vulkan_CreateSurface(self.handle, instance, nullptr, &surface)) { LOG_ERROR("{}", SDL_GetError()); return nullptr; } @@ -237,10 +196,10 @@ auto Window::get_surface(VkInstance instance) -> VkSurfaceKHR { return surface; } -auto Window::get_handle() -> void * { +auto Window::get_handle(this Window &self) -> void * { ZoneScoped; - auto window_props = SDL_GetWindowProperties(impl->handle); + auto window_props = SDL_GetWindowProperties(self.handle); #ifdef LS_LINUX const std::string_view video_driver = SDL_GetCurrentVideoDriver(); @@ -258,7 +217,7 @@ auto Window::get_handle() -> void * { return nullptr; } -auto Window::show_dialog(const ShowDialogInfo &info) -> void { +auto Window::show_dialog(this Window &self, const ShowDialogInfo &info) -> void { ZoneScoped; memory::ScopedStack stack; @@ -289,7 +248,7 @@ auto Window::show_dialog(const ShowDialogInfo &info) -> void { SDL_SetPointerProperty(props, SDL_PROP_FILE_DIALOG_FILTERS_POINTER, sdl_filters.data()); SDL_SetNumberProperty(props, SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER, static_cast(sdl_filters.size())); - SDL_SetPointerProperty(props, SDL_PROP_FILE_DIALOG_WINDOW_POINTER, impl->handle); + SDL_SetPointerProperty(props, SDL_PROP_FILE_DIALOG_WINDOW_POINTER, self.handle); SDL_SetStringProperty(props, SDL_PROP_FILE_DIALOG_LOCATION_STRING, spawn_path_str.c_str()); SDL_SetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, info.multi_select); SDL_SetStringProperty(props, SDL_PROP_FILE_DIALOG_TITLE_STRING, stack.null_terminate_cstr(info.title)); diff --git a/Lorr/Engine/Window/Window.hh b/Lorr/Engine/Window/Window.hh index 9ce6b3f0..9129490c 100644 --- a/Lorr/Engine/Window/Window.hh +++ b/Lorr/Engine/Window/Window.hh @@ -1,10 +1,11 @@ #pragma once -#include "Engine/Core/Handle.hh" - -#include - +#include #include +#include +#include + +#include namespace lr { enum class WindowCursor { @@ -41,17 +42,6 @@ struct SystemDisplay { f32 refresh_rate = 30.0f; }; -struct WindowCallbacks { - void *user_data = nullptr; - void (*on_resize)(void *user_data, glm::uvec2 size) = nullptr; - void (*on_mouse_pos)(void *user_data, glm::vec2 position, glm::vec2 relative) = nullptr; - void (*on_mouse_button)(void *user_data, u8 button, bool down) = nullptr; - void (*on_mouse_scroll)(void *user_data, glm::vec2 offset) = nullptr; - void (*on_text_input)(void *user_data, const c8 *text) = nullptr; - void (*on_key)(void *user_data, SDL_Keycode key_code, SDL_Scancode scan_code, u16 mods, bool down) = nullptr; - void (*on_close)(void *user_data) = nullptr; -}; - enum class DialogKind : u32 { OpenFile = 0, SaveFile, @@ -74,31 +64,52 @@ struct ShowDialogInfo { }; struct WindowInfo { - constexpr static i32 USE_PRIMARY_MONITOR = 0; - std::string title = {}; - std::string icon = {}; - i32 monitor = USE_PRIMARY_MONITOR; - u32 width = 0; - u32 height = 0; + SystemDisplay *display = nullptr; + i32 width = 0; + i32 height = 0; WindowFlag flags = WindowFlag::None; }; -struct Window : Handle { - static auto create(const WindowInfo &info) -> Window; - auto destroy() -> void; +struct Window { + constexpr static auto MODULE_NAME = "Window"; + + std::string title = {}; + i32 width = 0; + i32 height = 0; + WindowFlag flags = WindowFlag::None; + SystemDisplay *display = nullptr; + + SDL_Window *handle = nullptr; + ls::option swap_chain = ls::nullopt; + + WindowCursor current_cursor = WindowCursor::Arrow; + glm::uvec2 cursor_position = {}; + std::array cursors = {}; + std::vector> event_listeners = {}; + + static auto init_sdl() -> bool; + static auto display_at(i32 monitor_id) -> ls::option; + + Window(const WindowInfo &info); + auto init(this Window &) -> bool; + auto destroy(this Window &) -> void; + auto update(this Window &, f64) -> void; + + auto set_cursor(this Window &, WindowCursor cursor) -> void; + auto get_cursor(this Window &) -> WindowCursor; + auto show_cursor(this Window &, bool show) -> void; - auto poll(const WindowCallbacks &callbacks) -> void; - auto set_cursor(WindowCursor cursor) -> void; - auto get_cursor() -> WindowCursor; - auto show_cursor(bool show) -> void; + template + auto add_listener(T &listener) { + event_listeners.push_back([&listener](SDL_Event &e) { listener.window_event(e); }); + } - static auto display_at(i32 monitor_id = WindowInfo::USE_PRIMARY_MONITOR) -> ls::option; - auto get_size() -> glm::uvec2; - auto get_surface(VkInstance instance) -> VkSurfaceKHR; - auto get_handle() -> void *; + auto get_size(this Window &) -> glm::ivec2; + auto get_surface(this Window &, VkInstance instance) -> VkSurfaceKHR; + auto get_handle(this Window &) -> void *; - auto show_dialog(const ShowDialogInfo &info) -> void; + auto show_dialog(this Window &, const ShowDialogInfo &info) -> void; }; } // namespace lr diff --git a/Lorr/Runtime/RuntimeModule.cc b/Lorr/Runtime/RuntimeModule.cc new file mode 100644 index 00000000..8b60dbfa --- /dev/null +++ b/Lorr/Runtime/RuntimeModule.cc @@ -0,0 +1,83 @@ +#include "Runtime/RuntimeModule.hh" + +#include "Engine/Asset/Asset.hh" +#include "Engine/Core/App.hh" +#include "Engine/Graphics/ImGuiRenderer.hh" +#include "Engine/Graphics/VulkanDevice.hh" +#include "Engine/Scene/ECSModule/Core.hh" +#include "Engine/Window/Window.hh" + +auto RuntimeModule::init(this RuntimeModule &self) -> bool { + LOG_TRACE("Actvie world: {}", self.world_path); + + auto &asset_man = lr::App::mod(); + asset_man.import_project(self.world_path); + + return true; +} + +auto RuntimeModule::update(this RuntimeModule &self, f64 delta_time) -> void { + auto &window = lr::App::mod(); + auto &device = lr::App::mod(); + auto &imgui_renderer = lr::App::mod(); + auto &asset_man = lr::App::mod(); + auto &scene_renderer = lr::App::mod(); + + auto swapchain_attachment = device.new_frame(window.swap_chain.value()); + swapchain_attachment = vuk::clear_image(std::move(swapchain_attachment), vuk::Black); + imgui_renderer.begin_frame(delta_time, swapchain_attachment->extent); + + if (self.active_scene_uuid) { + auto *active_scene = asset_man.get_scene(self.active_scene_uuid); + auto camera_query = active_scene // + ->get_world() + .query_builder() + .build(); + + camera_query.each([&window](flecs::entity, lr::ECS::Camera &c, lr::ECS::ActiveCamera) { + c.resolution = glm::vec2(window.width, window.height); + c.aspect_ratio = c.resolution.x / c.resolution.y; + }); + + active_scene->tick(static_cast(delta_time)); + + auto prepared_frame = active_scene->prepare_frame(scene_renderer); + auto scene_render_info = lr::SceneRenderInfo{ + .delta_time = static_cast(delta_time), + .cull_flags = active_scene->get_cull_flags(), + }; + swapchain_attachment = scene_renderer.render(std::move(swapchain_attachment), scene_render_info, prepared_frame); + } + + if (ImGui::Begin("Runtime")) { + const auto ®istry = asset_man.get_registry(); + for (const auto &[asset_uuid, asset] : registry) { + if (asset.type != lr::AssetType::Scene) { + continue; + } + + const auto &path_str = asset.path.string(); + if (ImGui::Button(path_str.c_str())) { + if (self.active_scene_uuid) { + asset_man.unload_scene(self.active_scene_uuid); + } + + if (asset_man.load_scene(asset_uuid)) { + self.active_scene_uuid = asset_uuid; + } + } + } + } + ImGui::End(); + + swapchain_attachment = imgui_renderer.end_frame(std::move(swapchain_attachment)); + device.end_frame(std::move(swapchain_attachment)); +} + +auto RuntimeModule::destroy(this RuntimeModule &self) -> void { + auto &asset_man = lr::App::mod(); + + if (self.active_scene_uuid) { + asset_man.unload_scene(self.active_scene_uuid); + } +} diff --git a/Lorr/Runtime/RuntimeModule.hh b/Lorr/Runtime/RuntimeModule.hh new file mode 100644 index 00000000..9ade34b5 --- /dev/null +++ b/Lorr/Runtime/RuntimeModule.hh @@ -0,0 +1,15 @@ +#pragma once + +#include "Engine/Asset/UUID.hh" + +struct RuntimeModule { + static constexpr auto MODULE_NAME = "Runtime"; + + fs::path world_path = {}; + lr::UUID active_scene_uuid = lr::UUID(nullptr); + + RuntimeModule(fs::path world_path_) : world_path(std::move(world_path_)) {} + auto init(this RuntimeModule &) -> bool; + auto update(this RuntimeModule &, f64 delta_time) -> void; + auto destroy(this RuntimeModule &) -> void; +}; diff --git a/Lorr/Runtime/main.cc b/Lorr/Runtime/main.cc new file mode 100755 index 00000000..fe031601 --- /dev/null +++ b/Lorr/Runtime/main.cc @@ -0,0 +1,32 @@ +#include "Runtime/RuntimeModule.hh" + +#include "Engine/Asset/Asset.hh" +#include "Engine/Core/App.hh" +#include "Engine/Graphics/ImGuiRenderer.hh" +#include "Engine/Graphics/VulkanDevice.hh" +#include "Engine/Window/Window.hh" + +i32 main(i32 argc, c8 **argv) { + ZoneScoped; + + if (argc != 2) { + fmt::println("Invalid world, example: Runtime path/to/world.lrproj"); + return 1; + } + + auto world_path = fs::path(argv[1]); + + lr::Window::init_sdl(); + auto primary_display = lr::Window::display_at(0).value(); + + lr::AppBuilder() // + .module(3) + .module(lr::WindowInfo{ .title = "Example Game", .width = 1720, .height = 880, .flags = lr::WindowFlag::Centered }) + .module() + .module() + .module() + .module(std::move(world_path)) + .build(8, "runtime.log"); + + return 0; +} diff --git a/Lorr/Runtime/xmake.lua b/Lorr/Runtime/xmake.lua new file mode 100755 index 00000000..39d8141b --- /dev/null +++ b/Lorr/Runtime/xmake.lua @@ -0,0 +1,15 @@ +target("Runtime") + set_kind("binary") + set_languages("cxx23") + add_deps("Lorr") + add_includedirs("./") + add_files("**.cc") + add_rpathdirs("@executable_path") + + add_files("./Resources/**") + add_rules("lorr.install_resources", { + root_dir = os.scriptdir() .. "/Resources", + output_dir = "resources/runtime", + }) +target_end() + diff --git a/Lorr/xmake.lua b/Lorr/xmake.lua index 92081e7f..c5c86284 100755 --- a/Lorr/xmake.lua +++ b/Lorr/xmake.lua @@ -1,3 +1,4 @@ includes("Editor") +includes("Runtime") includes("Engine") includes("ls") diff --git a/xmake/packages.lua b/xmake/packages.lua index f08b456b..328b427a 100755 --- a/xmake/packages.lua +++ b/xmake/packages.lua @@ -36,13 +36,13 @@ add_requires("simdutf v6.2.0") add_requires("simdjson v3.12.2") add_requires("unordered_dense v4.5.0") add_requires("tracy v0.11.1", { configs = { - tracy_enable = has_config("profile"), - on_demand = has_config("profile"), - callstack = has_config("profile"), + tracy_enable = false, + on_demand = true, + callstack = false, callstack_inlines = false, - code_transfer = true, + code_transfer = false, exit = true, - system_tracing = true, + system_tracing = false, } }) add_requires("vk-bootstrap v1.4.307", { system = false, debug = is_mode("debug") }) add_requires("fastgltf v0.8.0") @@ -56,7 +56,7 @@ add_requires("libsdl3") add_requires("shader-slang v2025.12.1") add_requires("vuk 2025.07.09", { configs = { debug_allocations = false, - disable_exceptions = true, + disable_exceptions = false, }, debug = is_mode("debug") }) add_requires("meshoptimizer v0.24")