diff --git a/Lorr/Editor/EditorModule.cc b/Lorr/Editor/EditorModule.cc index 48ce6419..8e6e9f9c 100755 --- a/Lorr/Editor/EditorModule.cc +++ b/Lorr/Editor/EditorModule.cc @@ -365,12 +365,14 @@ static auto draw_welcome_popup(EditorModule &self) -> void { ZoneScoped; auto &window = lr::App::mod(); - ImGui::SetNextWindowSize({ 480.0f, 350.0f }, ImGuiCond_Appearing); + auto center_window = glm::vec2(window.get_size()) * 0.5f; + ImGui::SetNextWindowSize({ 480.0f, 350.0f }, ImGuiCond_Always); + ImGui::SetNextWindowPos({ center_window.x, center_window.y }, ImGuiCond_Always, { 0.5f, 0.5f }); constexpr auto popup_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings; if (ImGui::BeginPopupModal("###welcome", nullptr, popup_flags)) { // ── HEADERS ───────────────────────────────────────────────────────── ImGui::TextUnformatted("placeholder"); - ImGui::InvisibleButton("placeholder", { 0.0f, 75.0f }); + ImGui::InvisibleButton("placeholder", { -FLT_MAX, 75.0f }); // ── SECTIONS ──────────────────────────────────────────────────────── if (ImGui::BeginTabBar("project_guide")) { diff --git a/Lorr/Editor/Window/AssetBrowserWindow.cc b/Lorr/Editor/Window/AssetBrowserWindow.cc index e791e6e3..d2596ec6 100755 --- a/Lorr/Editor/Window/AssetBrowserWindow.cc +++ b/Lorr/Editor/Window/AssetBrowserWindow.cc @@ -242,7 +242,7 @@ static auto draw_dir_contents(AssetBrowserWindow &self) -> void { 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()) { + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) { ImGui::SetDragDropPayload("ASSET_BY_UUID", &asset->uuid, sizeof(lr::UUID)); ImGui::EndDragDropSource(); } diff --git a/Lorr/Editor/Window/InspectorWindow.cc b/Lorr/Editor/Window/InspectorWindow.cc index 80b85782..228fbbf6 100755 --- a/Lorr/Editor/Window/InspectorWindow.cc +++ b/Lorr/Editor/Window/InspectorWindow.cc @@ -80,10 +80,6 @@ static auto draw_inspector(InspectorWindow &) -> void { } lr::ECS::ComponentWrapper component(selected_entity, component_id); - if (!component.is_component()) { - return; - } - auto name_with_icon = stack.format_char("{}", component.name); ImGui::PushID(static_cast(component_id)); if (ImGui::CollapsingHeader(name_with_icon, nullptr, ImGuiTreeNodeFlags_DefaultOpen)) { @@ -95,43 +91,45 @@ static auto draw_inspector(InspectorWindow &) -> void { ImGui::TableSetupColumn("label", 0, 0.5f); ImGui::TableSetupColumn("property", ImGuiTableColumnFlags_WidthStretch); - ImGui::PushID(component.members_data); - component.for_each([&](usize &i, std::string_view member_name, lr::ECS::ComponentWrapper::Member &member) { - // Draw prop label - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - - ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y); - ImGui::TextUnformatted(member_name.data(), member_name.data() + member_name.length()); - ImGui::TableNextColumn(); - - bool component_modified = false; - ImGui::PushID(static_cast(i)); - 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); }, - [&](i64 *v) { component_modified |= ImGui::drag_vec(0, v, 1, ImGuiDataType_S64); }, - [&](u64 *v) { component_modified |= ImGui::drag_vec(0, v, 1, ImGuiDataType_U64); }, - [&](glm::vec2 *v) { component_modified |= ImGui::drag_vec(0, glm::value_ptr(*v), 2, ImGuiDataType_Float); }, - [&](glm::vec3 *v) { component_modified |= ImGui::drag_vec(0, glm::value_ptr(*v), 3, ImGuiDataType_Float); }, - [&](glm::vec4 *v) { component_modified |= ImGui::drag_vec(0, glm::value_ptr(*v), 4, ImGuiDataType_Float); }, - [](std::string *v) { ImGui::InputText("", v); }, - [&](lr::UUID *v) { component_modified |= inspect_asset(*v); }, - }, - member - ); + if (component.is_component()) { + ImGui::PushID(component.members_data); + component.for_each([&](usize &i, std::string_view member_name, lr::ECS::ComponentWrapper::Member &member) { + // Draw prop label + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetStyle().FramePadding.y); + ImGui::TextUnformatted(member_name.data(), member_name.data() + member_name.length()); + ImGui::TableNextColumn(); + + bool component_modified = false; + ImGui::PushID(static_cast(i)); + 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); }, + [&](i64 *v) { component_modified |= ImGui::drag_vec(0, v, 1, ImGuiDataType_S64); }, + [&](u64 *v) { component_modified |= ImGui::drag_vec(0, v, 1, ImGuiDataType_U64); }, + [&](glm::vec2 *v) { component_modified |= ImGui::drag_vec(0, glm::value_ptr(*v), 2, ImGuiDataType_Float); }, + [&](glm::vec3 *v) { component_modified |= ImGui::drag_vec(0, glm::value_ptr(*v), 3, ImGuiDataType_Float); }, + [&](glm::vec4 *v) { component_modified |= ImGui::drag_vec(0, glm::value_ptr(*v), 4, ImGuiDataType_Float); }, + [](std::string *v) { ImGui::InputText("", v); }, + [&](lr::UUID *v) { component_modified |= inspect_asset(*v); }, + }, + member + ); + ImGui::PopID(); + + if (component_modified) { + selected_entity.modified(component_id.entity()); + } + }); ImGui::PopID(); + } - if (component_modified) { - selected_entity.modified(component_id.entity()); - } - }); - - ImGui::PopID(); ImGui::EndTable(); if (ImGui::Button("Remove Component", ImVec2(region.x, 0))) { @@ -150,8 +148,7 @@ static auto draw_inspector(InspectorWindow &) -> void { ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos()); ImGui::SetNextWindowSize({ region.x, 0 }); if (ImGui::BeginPopup("add_component")) { - auto &entity_db = active_scene->get_entity_db(); - auto all_components = entity_db.get_components(); + auto all_components = active_scene->get_known_component_ids(); for (const auto &component : all_components) { // lr::memory::ScopedStack stack; ImGui::PushID(static_cast(component.raw_id())); diff --git a/Lorr/Editor/Window/SceneBrowserWindow.cc b/Lorr/Editor/Window/SceneBrowserWindow.cc index 72140321..516ecb67 100755 --- a/Lorr/Editor/Window/SceneBrowserWindow.cc +++ b/Lorr/Editor/Window/SceneBrowserWindow.cc @@ -25,7 +25,6 @@ static auto draw_children(SceneBrowserWindow &self, flecs::entity root) -> void auto q = world // .query_builder() .with(flecs::ChildOf, root) - .without() .build(); ImGui::TableNextRow(); diff --git a/Lorr/Editor/Window/ViewportWindow.cc b/Lorr/Editor/Window/ViewportWindow.cc index eeca6933..f9d1e7f4 100755 --- a/Lorr/Editor/Window/ViewportWindow.cc +++ b/Lorr/Editor/Window/ViewportWindow.cc @@ -9,6 +9,8 @@ #include "Engine/Asset/Asset.hh" #include "Engine/Core/App.hh" +#include "Engine/Math/Quat.hh" + #include #include @@ -124,37 +126,39 @@ static auto draw_tools(ViewportWindow &self) -> void { ImGui::SetNextWindowPos(editor_camera_popup_pos, ImGuiCond_Appearing); ImGui::SetNextWindowSize({ editor_camera_popup_width, 0 }, ImGuiCond_Appearing); if (ImGui::BeginPopup("editor_camera")) { - auto editor_camera = active_scene->get_editor_camera(); - auto *camera_transform = editor_camera.get_mut(); - auto *camera_info = editor_camera.get_mut(); - ImGui::SeparatorText("Position"); - ImGui::drag_vec(0, glm::value_ptr(camera_transform->position), 3, ImGuiDataType_Float); + ImGui::drag_vec(0, glm::value_ptr(self.editor_camera.position), 3, ImGuiDataType_Float); ImGui::SeparatorText("Rotation"); - auto camera_rotation_degrees = glm::degrees(camera_transform->rotation); - ImGui::drag_vec(1, glm::value_ptr(camera_rotation_degrees), 3, ImGuiDataType_Float); - camera_transform->rotation = glm::radians(lr::Math::normalize_180(camera_rotation_degrees)); + auto camera_yaw_pitch = glm::vec2(self.editor_camera.yaw, self.editor_camera.pitch); + ImGui::drag_vec(1, glm::value_ptr(camera_yaw_pitch), 2, ImGuiDataType_Float); + self.editor_camera.yaw = camera_yaw_pitch.x; + self.editor_camera.pitch = camera_yaw_pitch.y; ImGui::SeparatorText("FoV"); - ImGui::drag_vec(2, &camera_info->fov, 1, ImGuiDataType_Float); + ImGui::drag_vec(2, &self.editor_camera.fov, 1, ImGuiDataType_Float); ImGui::SeparatorText("Far Clip"); - ImGui::drag_vec(3, &camera_info->far_clip, 1, ImGuiDataType_Float); + ImGui::drag_vec(3, &self.editor_camera.far_clip, 1, ImGuiDataType_Float); - ImGui::SeparatorText("Velocity"); - ImGui::drag_vec(4, &camera_info->velocity_mul, 1, ImGuiDataType_Float); + ImGui::SeparatorText("Max Velocity"); + ImGui::drag_vec(4, &self.editor_camera.max_velocity, 1, ImGuiDataType_Float); ImGui::EndPopup(); } if (editor.show_debug) { auto &cull_flags = reinterpret_cast(active_scene->get_cull_flags()); + auto &scene_renderer = lr::App::mod(); + + ImGui::CheckboxFlags("Cull Mesh Frustum", &cull_flags, std::to_underlying(lr::GPU::CullFlags::MeshFrustum)); + ImGui::CheckboxFlags("Cull Mesh Occlusion", &cull_flags, std::to_underlying(lr::GPU::CullFlags::MeshOcclusion)); ImGui::CheckboxFlags("Cull Meshlet Frustum", &cull_flags, std::to_underlying(lr::GPU::CullFlags::MeshletFrustum)); + ImGui::CheckboxFlags("Cull Meshlet Occlusion", &cull_flags, std::to_underlying(lr::GPU::CullFlags::MeshletOcclusion)); 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", &editor.scene_renderer.debug_lines); + ImGui::Checkbox("Debug Lines", &scene_renderer.debug_lines); + ImGui::SliderFloat("Overdraw Heatmap", &scene_renderer.overdraw_heatmap_scale, 0.0f, 100.0f); } } @@ -168,18 +172,13 @@ static auto draw_viewport(ViewportWindow &self, vuk::Format format, vuk::Extent3 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(); auto *current_window = ImGui::GetCurrentWindow(); auto window_rect = current_window->InnerRect; auto window_pos = window_rect.Min; auto window_size = window_rect.GetSize(); auto work_area_size = ImGui::GetContentRegionAvail(); auto &io = ImGui::GetIO(); - - 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; + auto delta_time = io.DeltaTime; ImGuizmo::SetDrawlist(); ImGuizmo::SetOrthographic(false); @@ -200,7 +199,50 @@ static auto draw_viewport(ViewportWindow &self, vuk::Format format, vuk::Extent3 requested_texel_transform.emplace(glm::uvec2(mouse_pos_rel.x, mouse_pos_rel.y)); } - auto prepared_frame = active_scene->prepare_frame(scene_renderer); + { + // Update editor camera + auto target_velocity = glm::vec3(0.0f); + if (!ImGuizmo::IsUsingAny() && ImGui::IsWindowHovered()) { + if (ImGui::IsKeyDown(ImGuiKey_W)) { + target_velocity.x = self.editor_camera.max_velocity; + } + + if (ImGui::IsKeyDown(ImGuiKey_S)) { + target_velocity.x = -self.editor_camera.max_velocity; + } + + if (ImGui::IsKeyDown(ImGuiKey_D)) { + target_velocity.z = self.editor_camera.max_velocity; + } + + if (ImGui::IsKeyDown(ImGuiKey_A)) { + target_velocity.z = -self.editor_camera.max_velocity; + } + + if (ImGui::IsKeyDown(ImGuiKey_E)) { + target_velocity.y = self.editor_camera.max_velocity; + } + + if (ImGui::IsKeyDown(ImGuiKey_Q)) { + target_velocity.y = -self.editor_camera.max_velocity; + } + + if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) { + auto drag = ImGui::GetMouseDragDelta(ImGuiMouseButton_Left, 0); + ImGui::ResetMouseDragDelta(ImGuiMouseButton_Left); + + auto sensitivity = 0.1f; + self.editor_camera.yaw += drag.x * sensitivity; + self.editor_camera.pitch -= drag.y * sensitivity; + self.editor_camera.pitch = glm::clamp(self.editor_camera.pitch, -89.9f, 89.9f); + } + } + + self.editor_camera.resolution = { window_size.x, window_size.y }; + self.editor_camera.update(delta_time, target_velocity); + } + + auto prepared_frame = active_scene->prepare_frame(scene_renderer, self.editor_camera); // NOLINT(cppcoreguidelines-slicing) auto viewport_attachment_info = vuk::ImageAttachment{ .image_type = vuk::ImageType::e2D, @@ -214,7 +256,7 @@ static auto draw_viewport(ViewportWindow &self, vuk::Format format, vuk::Extent3 }; auto viewport_attachment = vuk::declare_ia("viewport", viewport_attachment_info); auto scene_render_info = lr::SceneRenderInfo{ - .delta_time = ImGui::GetIO().DeltaTime, + .delta_time = delta_time, .cull_flags = active_scene->get_cull_flags(), .picking_texel = requested_texel_transform, }; @@ -234,14 +276,18 @@ static auto draw_viewport(ViewportWindow &self, vuk::Format format, vuk::Extent3 } if (selected_entity && selected_entity.has()) { - auto camera_forward = glm::vec3(0.0, 0.0, 1.0) * lr::Math::quat_dir(camera_transform->rotation); - auto camera_projection = glm::perspective(glm::radians(camera->fov), camera->aspect_ratio, camera->far_clip, camera->near_clip); - auto camera_view = glm::lookAt(camera_transform->position, camera_transform->position + camera_forward, glm::vec3(0.0, 1.0, 0.0)); - camera_projection[1][1] *= -1.0f; + auto camera_direction = self.editor_camera.direction(); + auto camera_projection = glm::perspectiveRH_ZO( + glm::radians(self.editor_camera.fov), + self.editor_camera.aspect_ratio(), + self.editor_camera.far_clip, + self.editor_camera.near_clip + ); + auto camera_view = glm::lookAt(self.editor_camera.position, self.editor_camera.position + camera_direction, glm::vec3(0.0, 1.0, 0.0)); auto *transform = selected_entity.get_mut(); auto T = glm::translate(glm::mat4(1.0), transform->position); - auto R = glm::mat4_cast(glm::quat(transform->rotation)); + auto R = glm::mat4_cast(lr::Math::quat_dir(transform->rotation)); auto S = glm::scale(glm::mat4(1.0), transform->scale); auto gizmo_mat = T * R * S; auto delta_mat = glm::mat4(1.0f); @@ -274,51 +320,6 @@ static auto draw_viewport(ViewportWindow &self, vuk::Format format, vuk::Extent3 selected_entity.modified(); } } - - // ── CAMERA CONTROLLER ─────────────────────────────────────────────── - if (!ImGuizmo::IsUsingAny() && ImGui::IsWindowHovered()) { - bool reset_z = false; - bool reset_x = false; - - if (ImGui::IsKeyDown(ImGuiKey_W)) { - camera->axis_velocity.z = -camera->velocity_mul; - reset_z |= true; - } - - if (ImGui::IsKeyDown(ImGuiKey_S)) { - camera->axis_velocity.z = camera->velocity_mul; - reset_z |= true; - } - - if (ImGui::IsKeyDown(ImGuiKey_A)) { - camera->axis_velocity.x = -camera->velocity_mul; - reset_x |= true; - } - - if (ImGui::IsKeyDown(ImGuiKey_D)) { - camera->axis_velocity.x = camera->velocity_mul; - reset_x |= true; - } - - if (!reset_z) { - camera->axis_velocity.z = 0.0; - } - - if (!reset_x) { - camera->axis_velocity.x = 0.0; - } - - if (ImGui::IsMouseDragging(ImGuiMouseButton_Right)) { - auto drag = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right, 0); - ImGui::ResetMouseDragDelta(ImGuiMouseButton_Right); - - auto sensitivity = 0.1f; - auto camera_rotation_degrees = glm::degrees(camera_transform->rotation); - camera_rotation_degrees.x += drag.x * sensitivity; - camera_rotation_degrees.y += drag.y * sensitivity; - camera_transform->rotation = glm::radians(camera_rotation_degrees); - } - } } ViewportWindow::ViewportWindow(std::string name_, bool open_) : IWindow(std::move(name_), open_) { diff --git a/Lorr/Editor/Window/ViewportWindow.hh b/Lorr/Editor/Window/ViewportWindow.hh index d2040100..cf7232e7 100644 --- a/Lorr/Editor/Window/ViewportWindow.hh +++ b/Lorr/Editor/Window/ViewportWindow.hh @@ -1,10 +1,14 @@ #pragma once #include "Editor/Window/IWindow.hh" +#include "Engine/Scene/EditorCamera.hh" + +#include namespace led { struct ViewportWindow : IWindow { u32 gizmo_op = 0; + lr::EditorCamera editor_camera = {}; ViewportWindow(std::string name_, bool open_ = true); diff --git a/Lorr/Editor/main.cc b/Lorr/Editor/main.cc index 682e7f66..d789abd5 100755 --- a/Lorr/Editor/main.cc +++ b/Lorr/Editor/main.cc @@ -9,12 +9,17 @@ i32 main(i32, c8 **) { lr::Window::init_sdl(); auto primary_display = lr::Window::display_at(0).value(); + auto window_info = lr::WindowInfo{ + .title = "Lorr Editor", + .display = &primary_display, + .width = 1720, + .height = 880, + .flags = lr::WindowFlag::Centered | lr::WindowFlag::Resizable, + }; lr::AppBuilder() // - .module(3) - .module( - lr::WindowInfo{ .title = "Lorr Editor", .width = 1720, .height = 880, .flags = lr::WindowFlag::Centered | lr::WindowFlag::Resizable } - ) + .module(1) + .module(window_info) .module() .module() .module() diff --git a/Lorr/Engine/Asset/Asset.cc b/Lorr/Engine/Asset/Asset.cc index dd430904..b2191a9a 100755 --- a/Lorr/Engine/Asset/Asset.cc +++ b/Lorr/Engine/Asset/Asset.cc @@ -252,8 +252,6 @@ auto AssetManager::init_new_scene(this AssetManager &self, const UUID &uuid, con return false; } - scene->create_editor_camera(); - asset->acquire_ref(); return true; } @@ -801,7 +799,6 @@ auto AssetManager::load_model(this AssetManager &self, const UUID &uuid) -> bool ZoneNamedN(z, "GPU Meshlet Generation", true); auto &cur_lod = gpu_mesh.lods[lod_index]; - auto simplified_indices = std::vector(); if (lod_index == 0) { simplified_indices = std::vector(mesh_indices.begin(), mesh_indices.end()); @@ -840,6 +837,7 @@ auto AssetManager::load_model(this AssetManager &self, const UUID &uuid) -> bool simplified_indices.resize(result_index_count); } + gpu_mesh.vertex_count = mesh_vertices.size(); gpu_mesh.lod_count += 1; last_lod_indices = simplified_indices; @@ -881,8 +879,16 @@ auto AssetManager::load_model(this AssetManager &self, const UUID &uuid) -> bool auto meshlet_bb_min = glm::vec3(std::numeric_limits::max()); auto meshlet_bb_max = glm::vec3(std::numeric_limits::lowest()); for (u32 i = 0; i < raw_meshlet.triangle_count * 3; i++) { - const auto &tri_pos = mesh_vertices - [indirect_vertex_indices[raw_meshlet.vertex_offset + local_triangle_indices[raw_meshlet.triangle_offset + i]]]; + auto local_triangle_index_offset = raw_meshlet.triangle_offset + i; + LS_EXPECT(local_triangle_index_offset < local_triangle_indices.size()); + auto local_triangle_index = local_triangle_indices[local_triangle_index_offset]; + LS_EXPECT(local_triangle_index < raw_meshlet.vertex_count); + auto indirect_vertex_index_offset = raw_meshlet.vertex_offset + local_triangle_index; + LS_EXPECT(indirect_vertex_index_offset < indirect_vertex_indices.size()); + auto indirect_vertex_index = indirect_vertex_indices[indirect_vertex_index_offset]; + LS_EXPECT(indirect_vertex_index < vertex_count); + + const auto &tri_pos = mesh_vertices[indirect_vertex_index]; meshlet_bb_min = glm::min(meshlet_bb_min, tri_pos); meshlet_bb_max = glm::max(meshlet_bb_max, tri_pos); } @@ -978,7 +984,7 @@ auto AssetManager::load_model(this AssetManager &self, const UUID &uuid) -> bool 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)); + gpu_mesh_subrange = transfer_man.upload(std::move(cpu_mesh_buffer), std::move(gpu_mesh_subrange)); transfer_man.wait_on(std::move(gpu_mesh_subrange)); for (auto lod_index = 0_sz; lod_index < gpu_mesh.lod_count; lod_index++) { @@ -992,7 +998,7 @@ auto AssetManager::load_model(this AssetManager &self, const UUID &uuid) -> bool lod.indirect_vertex_indices += gpu_mesh_bda + mesh_upload_offset; auto gpu_lod_subrange = vuk::discard_buf("mesh lod subrange", gpu_mesh_buffer_handle->subrange(mesh_upload_offset, lod_upload_size)); - gpu_lod_subrange = transfer_man.upload_staging(std::move(lod_cpu_buffer), std::move(gpu_lod_subrange)); + gpu_lod_subrange = transfer_man.upload(std::move(lod_cpu_buffer), std::move(gpu_lod_subrange)); transfer_man.wait_on(std::move(gpu_lod_subrange)); mesh_upload_offset += lod_upload_size; @@ -1149,7 +1155,7 @@ auto AssetManager::load_texture(this AssetManager &self, const UUID &uuid, const } auto image_data = std::move(parsed_image->data); - auto buffer = transfer_man.alloc_transient_buffer(vuk::MemoryUsage::eCPUonly, ls::size_bytes(image_data)); + auto buffer = transfer_man.alloc_image_buffer(format, extent); std::memcpy(buffer->mapped_ptr, image_data.data(), image_data.size()); dst_attachment = vuk::copy(std::move(buffer), std::move(dst_attachment)); @@ -1175,7 +1181,7 @@ auto AssetManager::load_texture(this AssetManager &self, const UUID &uuid, const .depth = 1, }; auto size = vuk::compute_image_size(format, level_extent); - auto buffer = transfer_man.alloc_transient_buffer(vuk::MemoryUsage::eCPUonly, size); + auto buffer = transfer_man.alloc_image_buffer(format, level_extent); // TODO, WARN: size param might not be safe. Check with asan. std::memcpy(buffer->mapped_ptr, image_data.data() + mip_data_offset, size); diff --git a/Lorr/Engine/Graphics/ImGuiRenderer.cc b/Lorr/Engine/Graphics/ImGuiRenderer.cc index e4012116..9bc1274c 100644 --- a/Lorr/Engine/Graphics/ImGuiRenderer.cc +++ b/Lorr/Engine/Graphics/ImGuiRenderer.cc @@ -191,10 +191,8 @@ auto ImGuiRenderer::end_frame(this ImGuiRenderer &self, vuk::ValueBytesPerPixel; - 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 upload_buffer = transfer_man.alloc_image_buffer(vuk::Format::eR8G8B8A8Srgb, upload_extent); auto *buffer_ptr = reinterpret_cast(upload_buffer->mapped_ptr); for (auto y = 0_u32; y < upload_extent.height; y++) { auto *pixels = static_cast(texture->GetPixelsAt(upload_offset.x, upload_offset.y + static_cast(y))); @@ -272,8 +270,8 @@ auto ImGuiRenderer::end_frame(this ImGuiRenderer &self, vuk::Value ls::option bool { 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, @@ -286,23 +286,6 @@ auto Device::init_resources(this Device &self) -> std::expected std::expected { ZoneScoped; - VkPresentModeKHR present_mode = self.frame_count() == 1 ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR; vkb::SwapchainBuilder builder(self.handle, surface); builder.set_desired_min_image_count(self.frame_count()); builder.set_desired_format( @@ -512,7 +494,14 @@ auto Device::create_swap_chain(this Device &self, VkSurfaceKHR surface, ls::opti .colorSpace = vuk::ColorSpaceKHR::eSrgbNonlinear, } ); - builder.set_desired_present_mode(present_mode); + + if (self.frame_count() != 1) { + builder.set_desired_present_mode(VK_PRESENT_MODE_MAILBOX_KHR); + builder.add_fallback_present_mode(VK_PRESENT_MODE_IMMEDIATE_KHR); + } else { + builder.set_desired_present_mode(VK_PRESENT_MODE_FIFO_KHR); + } + builder.add_fallback_present_mode(VK_PRESENT_MODE_FIFO_KHR); builder.set_image_usage_flags(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); auto recycling = false; diff --git a/Lorr/Engine/Graphics/Vulkan/TransferManager.cc b/Lorr/Engine/Graphics/Vulkan/TransferManager.cc index 30329c82..f700879b 100644 --- a/Lorr/Engine/Graphics/Vulkan/TransferManager.cc +++ b/Lorr/Engine/Graphics/Vulkan/TransferManager.cc @@ -15,25 +15,36 @@ auto TransferManager::destroy(this TransferManager &self) -> void { self.release(); } -auto TransferManager::alloc_transient_buffer_raw(this TransferManager &self, vuk::MemoryUsage usage, usize size, vuk::source_location LOC) - -> vuk::Buffer { +auto TransferManager::alloc_transient_buffer(this TransferManager &self, vuk::MemoryUsage usage, usize size, vuk::source_location LOC) noexcept + -> vuk::Value { ZoneScoped; - std::shared_lock _(self.mutex); - auto buffer = - *vuk::allocate_buffer(*self.device->allocator, { .mem_usage = usage, .size = size, .alignment = self.device->non_coherent_atom_size() }, LOC); - return *buffer; + auto buffer = vuk::Buffer{}; + auto buffer_info = vuk::BufferCreateInfo{ .mem_usage = usage, .size = size, .alignment = self.device->non_coherent_atom_size() }; + self.frame_allocator->allocate_buffers({ &buffer, 1 }, { &buffer_info, 1 }, LOC); + + return vuk::acquire_buf("transient buffer", buffer, vuk::eNone, LOC); } -auto TransferManager::alloc_transient_buffer(this TransferManager &self, vuk::MemoryUsage usage, usize size, vuk::source_location LOC) +auto TransferManager::alloc_image_buffer(this TransferManager &self, vuk::Format format, vuk::Extent3D extent, vuk::source_location LOC) noexcept -> vuk::Value { ZoneScoped; - auto buffer = self.alloc_transient_buffer_raw(usage, size, LOC); - return vuk::acquire_buf("transient buffer", buffer, vuk::Access::eNone, LOC); + auto write_lock = std::unique_lock(self.mutex); + auto alignment = vuk::format_to_texel_block_size(format); + auto size = vuk::compute_image_size(format, extent); + + auto buffer_handle = vuk::Buffer{}; + auto buffer_info = vuk::BufferCreateInfo{ .mem_usage = vuk::MemoryUsage::eCPUtoGPU, .size = size, .alignment = alignment }; + self.device->allocator->allocate_buffers({ &buffer_handle, 1 }, { &buffer_info, 1 }, LOC); + + auto buffer = vuk::acquire_buf("image buffer", buffer_handle, vuk::eNone, LOC); + self.image_buffers.emplace(buffer); + + return buffer; } -auto TransferManager::upload_staging(this TransferManager &, vuk::Value &&src, vuk::Value &&dst, vuk::source_location LOC) +auto TransferManager::upload(this TransferManager &, vuk::Value &&src, vuk::Value &&dst, vuk::source_location LOC) -> vuk::Value { ZoneScoped; @@ -50,53 +61,30 @@ auto TransferManager::upload_staging(this TransferManager &, vuk::Value &&src, Buffer &dst, u64 dst_offset, vuk::source_location LOC) - -> vuk::Value { - ZoneScoped; - - auto dst_handle = self.device->buffer(dst.id()); - auto dst_buffer = vuk::discard_buf("dst", dst_handle->subrange(dst_offset, src->size), LOC); - return self.upload_staging(std::move(src), std::move(dst_buffer), LOC); -} - -auto TransferManager::upload_staging( - this TransferManager &self, - void *data, - u64 data_size, - vuk::Value &&dst, - u64 dst_offset, - vuk::source_location LOC -) -> vuk::Value { - ZoneScoped; - - auto cpu_buffer = self.alloc_transient_buffer(vuk::MemoryUsage::eCPUonly, data_size, LOC); - std::memcpy(cpu_buffer->mapped_ptr, data, data_size); - - auto dst_buffer = vuk::discard_buf("dst", dst->subrange(dst_offset, cpu_buffer->size), LOC); - return self.upload_staging(std::move(cpu_buffer), std::move(dst_buffer), LOC); -} - -auto TransferManager::upload_staging(this TransferManager &self, void *data, u64 data_size, Buffer &dst, u64 dst_offset, vuk::source_location LOC) - -> vuk::Value { - ZoneScoped; - - auto cpu_buffer = self.alloc_transient_buffer(vuk::MemoryUsage::eCPUonly, data_size, LOC); - std::memcpy(cpu_buffer->mapped_ptr, data, data_size); - - auto dst_handle = self.device->buffer(dst.id()); - auto dst_buffer = vuk::discard_buf("dst", dst_handle->subrange(dst_offset, cpu_buffer->size), LOC); - return self.upload_staging(std::move(cpu_buffer), std::move(dst_buffer), LOC); -} - -auto TransferManager::upload_staging(this TransferManager &self, ImageView &image_view, void *data, u64, vuk::source_location LOC) +[[nodiscard]] +auto TransferManager::upload(this TransferManager &, vuk::Value &&src, vuk::Value &&dst, vuk::source_location LOC) -> vuk::Value { ZoneScoped; - std::shared_lock _(self.mutex); - auto dst_attachment_info = image_view.to_attachment(*self.device, vuk::ImageUsageFlagBits::eTransferDst); - auto result = vuk::host_data_to_image(self.device->allocator.value(), vuk::DomainFlagBits::eGraphicsQueue, dst_attachment_info, data, LOC); - result = vuk::generate_mips(std::move(result), 0, image_view.mip_count() - 1); - return result; + auto upload_pass = vuk::make_pass( + "upload", + [](vuk::CommandBuffer &cmd_list, // + VUK_BA(vuk::eTransferRead) src, + VUK_IA(vuk::eTransferWrite) dst) { + auto buffer_copy_region = vuk::BufferImageCopy{ + .bufferOffset = src->offset, + .imageSubresource = { .aspectMask = vuk::ImageAspectFlagBits::eColor, .layerCount = 1 }, + .imageOffset = {}, + .imageExtent = dst->extent, + }; + cmd_list.copy_buffer_to_image(src, dst, buffer_copy_region); + return dst; + }, + vuk::DomainFlagBits::eAny, + LOC + ); + + return upload_pass(std::move(src), std::move(dst)); } auto TransferManager::scratch_buffer(this TransferManager &self, const void *data, u64 size, vuk::source_location LOC) -> vuk::Value { @@ -134,7 +122,7 @@ auto TransferManager::wait_on(this TransferManager &self, vuk::UntypedValue &&fu thread_local vuk::Compiler transfer_man_compiler; fut.wait(self.device->get_allocator(), transfer_man_compiler); #else - std::unique_lock _(self.mutex); + auto write_lock = std::unique_lock(self.mutex); self.futures.push_back(std::move(fut)); #endif } @@ -142,7 +130,7 @@ auto TransferManager::wait_on(this TransferManager &self, vuk::UntypedValue &&fu auto TransferManager::wait_for_ops(this TransferManager &self, vuk::Compiler &compiler) -> void { ZoneScoped; - std::unique_lock _(self.mutex); + auto write_lock = std::unique_lock(self.mutex); vuk::wait_for_values_explicit(*self.frame_allocator, compiler, self.futures, {}); self.futures.clear(); } @@ -150,15 +138,28 @@ auto TransferManager::wait_for_ops(this TransferManager &self, vuk::Compiler &co auto TransferManager::acquire(this TransferManager &self, vuk::DeviceSuperFrameResource &super_frame_resource) -> void { ZoneScoped; - std::unique_lock _(self.mutex); + auto write_lock = std::unique_lock(self.mutex); auto &frame_resource = super_frame_resource.get_next_frame(); self.frame_allocator.emplace(frame_resource); + + for (auto it = self.image_buffers.begin(); it != self.image_buffers.end();) { + auto image_buffer = &*it; + if (*image_buffer->poll() == vuk::Signal::Status::eHostAvailable) { + auto evaluated_buffer = vuk::eval(image_buffer->get_head()); + LS_EXPECT(evaluated_buffer.holds_value()); + self.device->allocator->deallocate({ &evaluated_buffer.value(), 1 }); + it = self.image_buffers.erase(it); + continue; + } + + ++it; + } } auto TransferManager::release(this TransferManager &self) -> void { ZoneScoped; - std::unique_lock _(self.mutex); + auto write_lock = std::unique_lock(self.mutex); self.frame_allocator.reset(); } diff --git a/Lorr/Engine/Graphics/VulkanDevice.hh b/Lorr/Engine/Graphics/VulkanDevice.hh index b3ce1c43..5b53f689 100644 --- a/Lorr/Engine/Graphics/VulkanDevice.hh +++ b/Lorr/Engine/Graphics/VulkanDevice.hh @@ -21,6 +21,7 @@ private: mutable std::shared_mutex mutex = {}; std::vector futures = {}; + plf::colony> image_buffers = {}; ls::option frame_allocator; @@ -34,42 +35,25 @@ public: auto destroy(this TransferManager &) -> void; [[nodiscard]] - auto alloc_transient_buffer_raw(this TransferManager &, vuk::MemoryUsage usage, usize size, LR_THISCALL) -> vuk::Buffer; + auto alloc_transient_buffer(this TransferManager &, vuk::MemoryUsage usage, usize size, LR_THISCALL) noexcept -> vuk::Value; [[nodiscard]] - auto alloc_transient_buffer(this TransferManager &, vuk::MemoryUsage usage, usize size, LR_THISCALL) -> vuk::Value; + auto alloc_image_buffer(this TransferManager &, vuk::Format format, vuk::Extent3D extent, LR_THISCALL) noexcept -> vuk::Value; [[nodiscard]] - auto upload_staging(this TransferManager &, vuk::Value &&src, vuk::Value &&dst, LR_THISCALL) -> vuk::Value; + auto upload(this TransferManager &, vuk::Value &&src, vuk::Value &&dst, LR_THISCALL) -> vuk::Value; [[nodiscard]] - auto upload_staging(this TransferManager &, vuk::Value &&src, Buffer &dst, u64 dst_offset = 0, LR_THISCALL) - -> vuk::Value; - - [[nodiscard]] - auto upload_staging(this TransferManager &, void *data, u64 data_size, vuk::Value &&dst, u64 dst_offset = 0, LR_THISCALL) - -> vuk::Value; - - [[nodiscard]] - auto upload_staging(this TransferManager &, void *data, u64 data_size, Buffer &dst, u64 dst_offset = 0, LR_THISCALL) -> vuk::Value; - - [[nodiscard]] - auto upload_staging(this TransferManager &, ImageView &image_view, void *data, u64 data_size, LR_THISCALL) -> vuk::Value; - - template - [[nodiscard]] auto upload_staging(this TransferManager &self, ls::span span, Buffer &dst, u64 dst_offset = 0, LR_THISCALL) - -> vuk::Value { - ZoneScoped; - - return self.upload_staging(reinterpret_cast(span.data()), span.size_bytes(), dst, dst_offset, LOC); - } + auto upload(this TransferManager &, vuk::Value &&src, vuk::Value &&dst, LR_THISCALL) + -> vuk::Value; template - [[nodiscard]] auto upload_staging(this TransferManager &self, ls::span span, vuk::Value &&dst, u64 dst_offset = 0, LR_THISCALL) - -> vuk::Value { + [[nodiscard]] auto upload(this TransferManager &self, ls::span span, vuk::Value &&dst, LR_THISCALL) -> vuk::Value { ZoneScoped; - return self.upload_staging(reinterpret_cast(span.data()), span.size_bytes(), std::move(dst), dst_offset, LOC); + auto src = self.alloc_transient_buffer(vuk::MemoryUsage::eCPUtoGPU, span.size_bytes(), LOC); + std::memcpy(src->mapped_ptr, span.data(), span.size_bytes()); + return self.upload(std::move(src), std::move(dst), LOC); } template diff --git a/Lorr/Engine/Math/Quat.hh b/Lorr/Engine/Math/Quat.hh index ce2c2cc4..3a8dff31 100644 --- a/Lorr/Engine/Math/Quat.hh +++ b/Lorr/Engine/Math/Quat.hh @@ -8,8 +8,8 @@ inline auto quat_dir(const glm::vec3 &rotation) -> glm::quat { ZoneScoped; glm::quat orientation = {}; - orientation = glm::angleAxis(rotation.x, glm::vec3(0.0f, 1.0f, 0.0f)); - orientation = glm::angleAxis(rotation.y, glm::vec3(1.0f, 0.0f, 0.0f)) * orientation; + orientation = glm::angleAxis(rotation.x, glm::vec3(1.0f, 0.0f, 0.0f)); + orientation = glm::angleAxis(rotation.y, glm::vec3(0.0f, 1.0f, 0.0f)) * orientation; orientation = glm::angleAxis(rotation.z, glm::vec3(0.0f, 0.0f, 1.0f)) * orientation; return glm::normalize(orientation); } diff --git a/Lorr/Engine/Math/Rotation.hh b/Lorr/Engine/Math/Rotation.hh deleted file mode 100644 index c22839ad..00000000 --- a/Lorr/Engine/Math/Rotation.hh +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -namespace lr::Math { -// Normalize a vector in degrees form to [-180, 180] -template -constexpr auto normalize_180(const glm::vec &rot) -> glm::vec { - return glm::mod(rot + 180.0f, glm::vec(360.0f)) - 180.0f; -} - -// Normalize a vector in degrees form to [-90, 90] -template -constexpr auto normalize_90(const glm::vec &rot) -> glm::vec { - return glm::mod(rot + 90.0f, glm::vec(180.0f)) - 90.0f; -} -} // namespace lr::Math diff --git a/Lorr/Engine/Resources/shaders/assert.slang b/Lorr/Engine/Resources/shaders/assert.slang index d1c6307c..653daadb 100644 --- a/Lorr/Engine/Resources/shaders/assert.slang +++ b/Lorr/Engine/Resources/shaders/assert.slang @@ -1,6 +1,6 @@ #ifdef ENABLE_ASSERTIONS #define assert_msg(x, msg, ...) do { if (!bool(x)) { printf(msg, __VA_ARGS__); } } while(false) -#define assert(x) assert_msg(x, "Shader aborted at " __FILE__ ":%d", __LINE__) +#define assert(x) assert_msg(x, "Shader aborted at " __FILE__ ":%d\n", __LINE__) #else #define assert_msg(...) #define assert(...) diff --git a/Lorr/Engine/Resources/shaders/cull.slang b/Lorr/Engine/Resources/shaders/cull.slang index b84547a7..0badc40a 100644 --- a/Lorr/Engine/Resources/shaders/cull.slang +++ b/Lorr/Engine/Resources/shaders/cull.slang @@ -88,7 +88,8 @@ public func test_frustum(in f32x4x4 mvp, in f32x3 aabb_center, in f32x3 aabb_ext public func test_occlusion( in ScreenAabb screen_aabb, in Image2D hiz_image, - in Sampler hiz_sampler + in Sampler hiz_sampler, + in constexpr bool ceiling ) -> bool { var hiz_size = u32x2(0.0); var hiz_levels = 0; @@ -96,19 +97,14 @@ public func test_occlusion( let min_uv = screen_aabb.min.xy; let max_uv = screen_aabb.max.xy; - let min_texel = u32x2(clamp(min_uv * f32x2(hiz_size), 0.0, hiz_size - 1.0)); - let max_texel = u32x2(clamp(max_uv * f32x2(hiz_size), 0.0, hiz_size - 1.0)); + let min_texel = u32x2(max(min_uv * f32x2(hiz_size), 0.0)); + let max_texel = u32x2(min(max_uv * f32x2(hiz_size), hiz_size - 1.0)); - let size = max_texel - min_texel + 1; + let size = max_texel - min_texel; let max_size = max(size.x, size.y); - var mip = firstbithigh(max_size - 1) - 1; - let smin = min_texel >> mip; - let smax = max_texel >> mip; - if (any(smax - smin > 1)) { - mip += 1; - } + let mip = max(floor(log2(max_size)) - 1.0, 0.0); var uv = (min_uv + max_uv) * 0.5; - let d = hiz_image.sample_mip(hiz_sampler, uv, mip); - return screen_aabb.max.z <= d; + let d = hiz_image.SampleLevel(hiz_sampler, uv, mip); + return screen_aabb.max.z < d; } diff --git a/Lorr/Engine/Resources/shaders/debug_drawer.slang b/Lorr/Engine/Resources/shaders/debug_drawer.slang index 0b1e1817..00381da2 100644 --- a/Lorr/Engine/Resources/shaders/debug_drawer.slang +++ b/Lorr/Engine/Resources/shaders/debug_drawer.slang @@ -38,7 +38,7 @@ public struct DebugDrawer { }; func push_draw(__ref DebugDrawData draw_data) -> u32 { - let index = std::atomic_add(draw_data.draw_count, 1, std::memory_order_acq_rel); + let index = __atomic_add(draw_data.draw_count, 1, MemoryOrder::AcquireRelease); if (index < draw_data.capacity) { return index; } @@ -49,7 +49,7 @@ func push_draw(__ref DebugDrawData draw_data) -> u32 { public func debug_draw_aabb(__ref DebugDrawer drawer, in DebugAABB v) -> void { let index = push_draw(drawer.aabb_data); if (index != ~0u) { - std::atomic_add(drawer.aabb_draw_cmd.instance_count, 1, std::memory_order_acq_rel); + __atomic_add(drawer.aabb_draw_cmd.instance_count, 1, MemoryOrder::AcquireRelease); drawer.aabb_buffer[index] = v; } } @@ -57,7 +57,7 @@ public func debug_draw_aabb(__ref DebugDrawer drawer, in DebugAABB v) -> void { public func debug_draw_rect(__ref DebugDrawer drawer, in DebugRect v) -> void { let index = push_draw(drawer.rect_data); if (index != ~0u) { - std::atomic_add(drawer.rect_draw_cmd.instance_count, 1, std::memory_order_acq_rel); + __atomic_add(drawer.rect_draw_cmd.instance_count, 1, MemoryOrder::AcquireRelease); drawer.rect_buffer[index] = v; } } \ No newline at end of file diff --git a/Lorr/Engine/Resources/shaders/gpu/image.slang b/Lorr/Engine/Resources/shaders/gpu/image.slang index 644af432..f08cd6a8 100644 --- a/Lorr/Engine/Resources/shaders/gpu/image.slang +++ b/Lorr/Engine/Resources/shaders/gpu/image.slang @@ -2,93 +2,20 @@ implementing gpu; import std; -public enum ImageOperand : u32 { - None = 0x0, - Bias = 0x1, - Lod = 0x2, - Grad = 0x4, - ConstOffset = 0x8, - Offset = 0x10, - ConstOffsets = 0x20, - MinLod = 0x80, - - // SPIR-V 1.5 - SPV_KHR_vulkan_memory_model - MakeTexelAvailable = 0x100, // Requires NonPrivateTexel to also be set. - MakeTexelVisible = 0x200, // Requires NonPrivateTexel to also be set. - NonPrivateTexel = 0x400, - VolatileTexel = 0x800, -}; - -// Image ──────────────────────────────────────────────────────────── -public typealias Image = _Texture; - -public extension Image { - public func sample(in Sampler sampler, vector tex_coords) -> T { - return this.Sample(sampler, tex_coords); - } - - public func sample_mip(in Sampler sampler, vector tex_coords, f32 mip) -> T { - return this.SampleLevel(sampler, tex_coords, mip); - } - - public func sample_grad( - in Sampler sampler, - vector tex_coords, - vector ddx, - vector ddy - ) -> T { - return this.SampleGrad(sampler, tex_coords, ddx, ddy); - } -}; - // Image1D ────────────────────────────────────────────────────────── -__generic -public typealias Image1D = Image; - -public extension Image1D { -}; +public typealias Image1D = Texture1D; // Image2D ────────────────────────────────────────────────────────── -public typealias Image2D = Image; - -public extension Image2D { - public func load(u32x2 texel, u32 mip = 0) -> T { - let coord = __vectorReshape<2>(texel); - return spirv_asm { - %sampled: __sampledType(T) = OpImageFetch $this $coord Lod $mip; - __truncate $$T result __sampledType(T) %sampled; - }; - } -}; +public typealias Image2D = Texture2D; // Image3D ────────────────────────────────────────────────────────── -public typealias Image3D = Image; - -public extension Image3D { -}; - -// StorageImage ──────────────────────────────────────────────────────────── -public typealias StorageImage = _Texture; -public extension StorageImage { - public func load(vector texel, MemoryScope scope = MemoryScope::Device) -> T { - return spirv_asm { - %sampled:__sampledType(T) = OpImageRead $this $texel NonPrivateTexel|MakeTexelVisible $scope; - __truncate $$T result __sampledType(T) %sampled; - }; - } - - public func store(vector texel, T value, MemoryScope scope = MemoryScope::Device) -> void { - spirv_asm { - OpImageWrite $this $texel __convertTexel(value) NonPrivateTexel|MakeTexelAvailable $scope; - }; - } -}; +public typealias Image3D = Texture3D; // StorageImage1D ─────────────────────────────────────────────────── -public typealias StorageImage1D = StorageImage; +public typealias StorageImage1D = RWTexture1D; // StorageImage2D ─────────────────────────────────────────────────── -public typealias StorageImage2D = StorageImage; +public typealias StorageImage2D = RWTexture2D; // StorageImage3D ─────────────────────────────────────────────────── -public typealias StorageImage3D = StorageImage; +public typealias StorageImage3D = RWTexture3D; diff --git a/Lorr/Engine/Resources/shaders/passes/brdf.slang b/Lorr/Engine/Resources/shaders/passes/brdf.slang index 82ffe8d4..3bd6a67f 100644 --- a/Lorr/Engine/Resources/shaders/passes/brdf.slang +++ b/Lorr/Engine/Resources/shaders/passes/brdf.slang @@ -26,21 +26,21 @@ ParameterBlock params; [[shader("fragment")]] func fs_main(VertexOutput input) -> f32x4 { - let pixel_pos = u32x2(input.position.xy); - let depth = params.depth_image.load(pixel_pos); + let pixel_pos = u32x3(u32x2(input.position.xy), 0); + let depth = params.depth_image.Load(pixel_pos); if (depth == 0.0) { discard; } - let albedo_color = params.albedo_image.sample_mip(params.linear_repeat_sampler, input.tex_coord, 0).rgb; + let albedo_color = params.albedo_image.SampleLevel(params.linear_repeat_sampler, input.tex_coord, 0).rgb; - let mapped_smooth_normal = params.normal_image.load(pixel_pos); + let mapped_smooth_normal = params.normal_image.Load(pixel_pos); let mapped_normal = std::oct_to_vec3(mapped_smooth_normal.xy); let smooth_normal = std::oct_to_vec3(mapped_smooth_normal.zw); - let emission = params.emissive_image.load(pixel_pos); + let emission = params.emissive_image.Load(pixel_pos); - let metallic_roughness_occlusion = params.metallic_roughness_occlusion_image.load(pixel_pos); + let metallic_roughness_occlusion = params.metallic_roughness_occlusion_image.Load(pixel_pos); let metallic = metallic_roughness_occlusion.x; let roughness = metallic_roughness_occlusion.y; let occlusion = metallic_roughness_occlusion.z; @@ -50,15 +50,15 @@ func fs_main(VertexOutput input) -> f32x4 { let world_position = world_position_h.xyz / world_position_h.w; // PBR constants - const f32x3 V = normalize(params.camera.position - world_position); - const f32x3 L = normalize(params.environment.sun_direction); // temp - const f32x3 N = mapped_normal; + let V = normalize(params.camera.position - world_position); + let L = normalize(params.environment.sun_direction); // temp + let N = normalize(mapped_normal); var sun_illuminance = f32x3(1.0); - var sky_luminance = f32x3(1.0); + var sky_luminance = f32x3(0.1); if (params.environment.flags & (EnvironmentFlags::HasSun | EnvironmentFlags::HasAtmosphere)) { // SUN LIGHT COLOR ────────────────────────────────────────────────── - var eye_altitude = world_position.y * CAMERA_SCALE_UNIT; + var eye_altitude = max(world_position.y, 0.0) * 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); @@ -67,7 +67,7 @@ func fs_main(VertexOutput input) -> f32x4 { 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; + f32x3 sun_transmittance = params.sky_transmittance_lut.SampleLevel(params.linear_clamp_sampler, transmittance_uv, 0.0).rgb; sun_illuminance = sun_transmittance * params.environment.sun_intensity; // SKY AMBIENT COLOR ──────────────────────────────────────────────── @@ -79,31 +79,34 @@ func fs_main(VertexOutput input) -> f32x4 { 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_multiscattering = true; sky_info.eval_mie_phase = false; - let sky_result = integrate_single_scattered_luminance(params.environment, params.linear_clamp_sampler, sky_info); + let sky_result = integrate_single_scattered_luminance( + sky_info, params.environment, params.linear_clamp_sampler, params.sky_transmittance_lut, params.sky_multiscattering_lut); 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; + let ambient_contribution = sky_luminance * albedo_color * occlusion; // MATERIAL COLOR ─────────────────────────────────────────────────── // https://marmosetco.tumblr.com/post/81245981087 - const f32x3 R = reflect(-V, N); - const f32 horizon_fade = 1.3; - f32 horizon = saturate(1.0 + horizon_fade * dot(R, smooth_normal)); + let R = reflect(-V, N); + let horizon_fade = 1.3; + var horizon = saturate(1.0 + horizon_fade * dot(R, smooth_normal)); horizon *= horizon; - const f32 NoL = max(dot(N, L), 0.0); - f32x3 brdf = BRDF(V, N, L, albedo_color, roughness, metallic); - f32x3 material_surface_color = brdf * horizon * sun_illuminance * NoL; + var material_surface_color = f32x3(0.0); + let NoL = max(dot(N, L), 0.0); + if (NoL > 0.0) { + let brdf = BRDF(V, N, L, albedo_color, roughness, metallic); + material_surface_color = brdf * horizon * sun_illuminance * NoL; + } // FINAL ──────────────────────────────────────────────────────────── - f32x3 final_color = material_surface_color + ambient_contribution + emission; + let final_color = material_surface_color + ambient_contribution + emission; return f32x4(final_color, 1.0); } diff --git a/Lorr/Engine/Resources/shaders/passes/copy.slang b/Lorr/Engine/Resources/shaders/passes/copy.slang index 4d3ef70e..7868f9e0 100644 --- a/Lorr/Engine/Resources/shaders/passes/copy.slang +++ b/Lorr/Engine/Resources/shaders/passes/copy.slang @@ -15,5 +15,5 @@ func cs_main(u32x3 thread_id : SV_DispatchThreadID) -> void { return; } - dst_image.Store(thread_id.xy, src_image.load(thread_id.xy, 0)); + dst_image.Store(thread_id.xy, src_image.Load(u32x3(thread_id.xy, 0), 0)); } diff --git a/Lorr/Engine/Resources/shaders/passes/cull_meshes.slang b/Lorr/Engine/Resources/shaders/passes/cull_meshes.slang new file mode 100644 index 00000000..5a3a9be0 --- /dev/null +++ b/Lorr/Engine/Resources/shaders/passes/cull_meshes.slang @@ -0,0 +1,80 @@ +import std; +import gpu; +import scene; +import cull; +import debug_drawer; + +[[vk::binding(0)]] ConstantBuffer camera; +[[vk::binding(1)]] StructuredBuffer meshes; +[[vk::binding(2)]] StructuredBuffer transforms; +[[vk::binding(3)]] Image2D hiz_image; +[[vk::binding(4)]] Sampler hiz_sampler; +[[vk::binding(5)]] RWStructuredBuffer mesh_instances; +[[vk::binding(6)]] RWStructuredBuffer meshlet_instances; +[[vk::binding(7)]] RWStructuredBuffer visible_meshlet_instances_count; +[[vk::binding(8)]] RWStructuredBuffer debug_drawer; + +#ifndef CULLING_MESHES_COUNT +#define CULLING_MESHES_COUNT 64 +#endif + +[[shader("compute")]] +[[numthreads(CULLING_MESHES_COUNT, 1, 1)]] +func cs_main( + uint3 thread_id : SV_DispatchThreadID, + uniform u32 mesh_instances_count, + uniform CullFlags cull_flags +) -> void { + let mesh_instance_index = thread_id.x; + if (mesh_instance_index >= mesh_instances_count) { + return; + } + + let mesh_instance = &mesh_instances[mesh_instance_index]; + let mesh = meshes[mesh_instance.mesh_index]; + let transform = transforms[mesh_instance.transform_index]; + let mvp = mul(camera.projection_view_mat, transform.world); + + let cull_frustum = (cull_flags & CullFlags::MeshFrustum) != 0; + if (cull_frustum && !test_frustum(mvp, mesh.bounds.aabb_center, mesh.bounds.aabb_extent)) { + return; + } + + var lod_index = 0; +#if 1 + // Credits: + // - https://github.com/Sunset-Flock/Timberdoodle/blob/786f141e261dff4756e7f1a67dd7f7a5e1277956/src/scene/mesh_lod.hpp#L45 + let aabb_center = mul(transform.world, f32x4(mesh.bounds.aabb_center, 1.0)).xyz; + let aabb_extent_x = length(transform.world[0]) * mesh.bounds.aabb_extent.x; + let aabb_extent_y = length(transform.world[1]) * mesh.bounds.aabb_extent.y; + let aabb_extent_z = length(transform.world[2]) * mesh.bounds.aabb_extent.z; + let aabb_rough_extent = max(max(aabb_extent_x, aabb_extent_y), aabb_extent_z); + let aabb_rough_camera_distance = max(length(aabb_center - camera.position) - 0.5 * aabb_rough_extent, 0.0); + + // Avoiding the atan here + let rough_resolution = max(camera.resolution.x, camera.resolution.y); + let fov90_distance_to_screen_ratio = 2.0f; + let pixel_size_at_1m = fov90_distance_to_screen_ratio / rough_resolution; + let aabb_size_at_1m = (aabb_rough_extent / aabb_rough_camera_distance); + let rough_aabb_pixel_size = aabb_size_at_1m / pixel_size_at_1m; + + for (var i = 1; i < mesh.lod_count; i++) { + let mesh_lod = mesh.lods[i]; + let rough_pixel_error = rough_aabb_pixel_size * mesh_lod.error; + if (rough_pixel_error < camera.acceptable_lod_error) { + lod_index = i; + } else { + break; + } + } +#endif + mesh_instance.lod_index = lod_index; + let mesh_lod = mesh.lods[lod_index]; + let meshlet_count = mesh_lod.meshlet_count; + var base_meshlet_instance_offset = __atomic_add(visible_meshlet_instances_count[2], meshlet_count, MemoryOrder::Relaxed); + for (u32 i = 0; i < meshlet_count; i++) { + let offset = base_meshlet_instance_offset + i; + meshlet_instances[offset].mesh_instance_index = mesh_instance_index; + meshlet_instances[offset].meshlet_index = i; + } +} diff --git a/Lorr/Engine/Resources/shaders/passes/cull_meshlets.slang b/Lorr/Engine/Resources/shaders/passes/cull_meshlets.slang index d547632f..35c9f6cc 100644 --- a/Lorr/Engine/Resources/shaders/passes/cull_meshlets.slang +++ b/Lorr/Engine/Resources/shaders/passes/cull_meshlets.slang @@ -6,56 +6,64 @@ import debug_drawer; #include -struct ShaderParameters { - ConstantBuffer camera; - StructuredBuffer meshlet_instances; - StructuredBuffer mesh_instances; - StructuredBuffer meshes; - StructuredBuffer transforms; - Image2D hiz_image; - Sampler hiz_sampler; - StructuredBuffer meshlet_instances_count; - - RWStructuredBuffer cull_triangles_cmd; - RWStructuredBuffer visible_meshlet_instances_indices; - RWStructuredBuffer debug_drawer; -}; +[[vk::constant_id(0)]] const bool LATE = false; +[[vk::binding(0)]] ConstantBuffer camera; +[[vk::binding(1)]] StructuredBuffer meshlet_instances; +[[vk::binding(2)]] StructuredBuffer mesh_instances; +[[vk::binding(3)]] StructuredBuffer meshes; +[[vk::binding(4)]] StructuredBuffer transforms; +[[vk::binding(5)]] Image2D hiz_image; +[[vk::binding(6)]] Sampler hiz_sampler; +[[vk::binding(7)]] RWStructuredBuffer visible_meshlet_instances_count; +[[vk::binding(8)]] RWStructuredBuffer visible_meshlet_instances_indices; +[[vk::binding(9)]] RWStructuredBuffer meshlet_instance_visibility_mask; +[[vk::binding(10)]] RWStructuredBuffer cull_triangles_cmd; +[[vk::binding(11)]] RWStructuredBuffer debug_drawer; #ifndef CULLING_MESHLET_COUNT - #define CULLING_MESHLET_COUNT 64 +#define CULLING_MESHLET_COUNT 64 #endif [[shader("compute")]] [[numthreads(CULLING_MESHLET_COUNT, 1, 1)]] func cs_main( uint3 thread_id : SV_DispatchThreadID, - uniform ParameterBlock params, uniform CullFlags cull_flags ) -> void { - let meshlet_instance_count = params.meshlet_instances_count[0]; + let meshlet_instance_count = visible_meshlet_instances_count[2]; let meshlet_instance_index = thread_id.x; if (meshlet_instance_index >= meshlet_instance_count) { return; } - let meshlet_instance = params.meshlet_instances[meshlet_instance_index]; - let mesh_instance = params.mesh_instances[meshlet_instance.mesh_instance_index]; - let mesh = params.meshes[mesh_instance.mesh_index]; - let transform = params.transforms[mesh_instance.transform_index]; + let meshlet_instance = meshlet_instances[meshlet_instance_index]; + let mesh_instance = mesh_instances[meshlet_instance.mesh_instance_index]; + let transform = transforms[mesh_instance.transform_index]; + let mvp = mul(camera.projection_view_mat, transform.world); + + let mesh = meshes[mesh_instance.mesh_index]; let mesh_lod = mesh.lods[mesh_instance.lod_index]; let bounds = mesh_lod.meshlet_bounds[meshlet_instance.meshlet_index]; - var visible = true; - if (visible && (cull_flags & CullFlags::MeshletFrustum)) { - let cur_mvp = mul(params.camera.projection_view_mat, transform.world); - visible = test_frustum(cur_mvp, bounds.aabb_center, bounds.aabb_extent); + let cull_frustum = (cull_flags & CullFlags::MeshletFrustum) != 0; + let cull_occlusion = (cull_flags & CullFlags::MeshletOcclusion) != 0; + + let meshlet_instance_visibility_index = mesh_instance.meshlet_instance_visibility_offset + meshlet_instance.meshlet_index; + let mask_index = meshlet_instance_visibility_index / 32; + let bit_index = meshlet_instance_visibility_index - mask_index * 32; + let visibility_bit = 1 << bit_index; + let was_visible = (meshlet_instance_visibility_mask[mask_index] & visibility_bit) != 0; + + var visible = LATE ? true : was_visible; + if (visible && cull_frustum) { + visible = test_frustum(mvp, bounds.aabb_center, bounds.aabb_extent); } - if (visible && (cull_flags & CullFlags::Occlusion)) { - let prev_mvp = mul(params.camera.frustum_projection_view_mat, transform.world); - if (let screen_aabb = project_aabb(prev_mvp, params.camera.near_clip, bounds.aabb_center, bounds.aabb_extent)) { - visible = !test_occlusion(screen_aabb, params.hiz_image, params.hiz_sampler); - if (visible && true) { + if (LATE && visible && cull_occlusion) { + if (let screen_aabb = project_aabb(mvp, camera.near_clip, bounds.aabb_center, bounds.aabb_extent)) { + visible = !test_occlusion(screen_aabb, hiz_image, hiz_sampler, false); +#ifdef DEBUG_DRAW + if (visible) { let ndc_aabb_max = screen_aabb.max.xy * 2.0 - 1.0; let ndc_aabb_min = screen_aabb.min.xy * 2.0 - 1.0; var debug_rect = DebugRect(); @@ -63,13 +71,32 @@ func cs_main( debug_rect.extent = ndc_aabb_max - ndc_aabb_min; debug_rect.color = f32x3(1.0, 0.0, 0.0); debug_rect.coord = DebugDrawCoord::NDC; - debug_draw_rect(params.debug_drawer[0], debug_rect); + debug_draw_rect(debug_drawer[0], debug_rect); } +#endif } } - if (visible) { - let index = std::atomic_add(params.cull_triangles_cmd[0].x, 1, std::memory_order_relaxed); - params.visible_meshlet_instances_indices[index] = meshlet_instance_index; + if (visible && (!LATE || !was_visible)) { + var index = 0; + if (!LATE) { + index = __atomic_add(visible_meshlet_instances_count[0], 1, MemoryOrder::Relaxed); + } else { + let early_count = visible_meshlet_instances_count[0]; + let late_offset = __atomic_add(visible_meshlet_instances_count[1], 1, MemoryOrder::Relaxed); + index = early_count + late_offset; + } + + visible_meshlet_instances_indices[index] = meshlet_instance_index; + + __atomic_add(cull_triangles_cmd[0].x, 1, MemoryOrder::Relaxed); + } + + if (LATE) { + if (visible) { + __atomic_or(meshlet_instance_visibility_mask[mask_index], visibility_bit, MemoryOrder::AcquireRelease); + } else { + __atomic_and(meshlet_instance_visibility_mask[mask_index], ~visibility_bit, MemoryOrder::AcquireRelease); + } } } diff --git a/Lorr/Engine/Resources/shaders/passes/cull_triangles.slang b/Lorr/Engine/Resources/shaders/passes/cull_triangles.slang index e530f4ab..8c6da6aa 100644 --- a/Lorr/Engine/Resources/shaders/passes/cull_triangles.slang +++ b/Lorr/Engine/Resources/shaders/passes/cull_triangles.slang @@ -6,17 +6,16 @@ import scene; import passes.visbuffer; -struct ShaderParameters { - ConstantBuffer camera; - StructuredBuffer visible_meshlet_instances_indices; - StructuredBuffer meshlet_instances; - StructuredBuffer mesh_instances; - StructuredBuffer meshes; - StructuredBuffer transforms; - - RWStructuredBuffer draw_cmd; - RWStructuredBuffer reordered_indices; -}; +[[vk::constant_id(0)]] const bool LATE = false; +[[vk::binding(0)]] ConstantBuffer camera; +[[vk::binding(1)]] StructuredBuffer visible_meshlet_instances_count; +[[vk::binding(2)]] StructuredBuffer visible_meshlet_instances_indices; +[[vk::binding(3)]] StructuredBuffer meshlet_instances; +[[vk::binding(4)]] StructuredBuffer mesh_instances; +[[vk::binding(5)]] StructuredBuffer meshes; +[[vk::binding(6)]] StructuredBuffer transforms; +[[vk::binding(7)]] RWStructuredBuffer draw_cmd; +[[vk::binding(8)]] RWStructuredBuffer reordered_indices; groupshared u32 base_index_shared; groupshared u32 triangles_passed_shared; @@ -111,50 +110,55 @@ func test_triangle(in f32x3x3 positions, in f32x2 resolution, CullFlags cull_fla func cs_main( uint3 group_id : SV_GroupID, uint3 group_thread_id : SV_GroupThreadID, - uniform ParameterBlock params, uniform CullFlags cull_flags ) -> void { + var visible_meshlet_instance_index = group_id.x; + if (LATE) { + visible_meshlet_instance_index += visible_meshlet_instances_count[0]; + } + let local_index = group_thread_id.x; let triangle_index = local_index * 3; - let visible_meshlet_index = group_id.x; - let meshlet_instance_index = params.visible_meshlet_instances_indices[visible_meshlet_index]; - let meshlet_instance = params.meshlet_instances[meshlet_instance_index]; - let mesh_instance = params.mesh_instances[meshlet_instance.mesh_instance_index]; - let mesh = params.meshes[mesh_instance.mesh_index]; + + let meshlet_instance_index = visible_meshlet_instances_indices[visible_meshlet_instance_index]; + let meshlet_instance = meshlet_instances[meshlet_instance_index]; + let mesh_instance = mesh_instances[meshlet_instance.mesh_instance_index]; + let mesh = meshes[mesh_instance.mesh_index]; let mesh_lod = mesh.lods[mesh_instance.lod_index]; let meshlet = mesh_lod.meshlets[meshlet_instance.meshlet_index]; if (local_index == 0) { triangles_passed_shared = 0; - let transform = params.transforms[mesh_instance.transform_index]; - model_view_proj_shared = mul(params.camera.projection_view_mat, transform.world); + let transform = transforms[mesh_instance.transform_index]; + model_view_proj_shared = mul(camera.projection_view_mat, transform.world); } - std::control_barrier(std::memory_order_acq_rel); + GroupMemoryBarrierWithGroupSync(); var triangle_passed = false; var active_triangle_index = 0; if (local_index < meshlet.triangle_count) { let indices = meshlet.indices(mesh_lod, local_index); let positions = meshlet.positions(mesh, indices); - triangle_passed = test_triangle(positions, params.camera.resolution, cull_flags, local_index); + triangle_passed = test_triangle(positions, camera.resolution, cull_flags, local_index); + triangle_passed = true; if (triangle_passed) { - active_triangle_index = std::atomic_add(triangles_passed_shared, 1, std::memory_order_relaxed); + active_triangle_index = __atomic_add(triangles_passed_shared, 1, MemoryOrder::Relaxed); } } - std::control_barrier(std::memory_order_acq_rel); + GroupMemoryBarrierWithGroupSync(); if (local_index == 0) { - base_index_shared = std::atomic_add(params.draw_cmd[0].index_count, triangles_passed_shared * 3, std::memory_order_relaxed); + base_index_shared = __atomic_add(draw_cmd[0].index_count, triangles_passed_shared * 3, MemoryOrder::Relaxed); } - std::control_barrier(std::memory_order_acq_rel); + GroupMemoryBarrierWithGroupSync(); if (triangle_passed) { let index_offset = base_index_shared + active_triangle_index * 3; - params.reordered_indices[index_offset + 0] = (meshlet_instance_index << MESHLET_PRIMITIVE_BITS) | ((triangle_index + 0) & MESHLET_PRIMITIVE_MASK); - params.reordered_indices[index_offset + 1] = (meshlet_instance_index << MESHLET_PRIMITIVE_BITS) | ((triangle_index + 1) & MESHLET_PRIMITIVE_MASK); - params.reordered_indices[index_offset + 2] = (meshlet_instance_index << MESHLET_PRIMITIVE_BITS) | ((triangle_index + 2) & MESHLET_PRIMITIVE_MASK); + reordered_indices[index_offset + 0] = (meshlet_instance_index << MESHLET_PRIMITIVE_BITS) | ((triangle_index + 0) & MESHLET_PRIMITIVE_MASK); + reordered_indices[index_offset + 1] = (meshlet_instance_index << MESHLET_PRIMITIVE_BITS) | ((triangle_index + 1) & MESHLET_PRIMITIVE_MASK); + reordered_indices[index_offset + 2] = (meshlet_instance_index << MESHLET_PRIMITIVE_BITS) | ((triangle_index + 2) & MESHLET_PRIMITIVE_MASK); } } diff --git a/Lorr/Engine/Resources/shaders/passes/editor_grid.slang b/Lorr/Engine/Resources/shaders/passes/editor_grid.slang index a302c44f..a9a11105 100644 --- a/Lorr/Engine/Resources/shaders/passes/editor_grid.slang +++ b/Lorr/Engine/Resources/shaders/passes/editor_grid.slang @@ -3,6 +3,11 @@ module editor_grid; import std; import scene; +struct ShaderParameters { + ConstantBuffer camera; +}; +uniform ParameterBlock params; + struct VertexOutput { f32x4 position : SV_Position; f32x3 near_pos; @@ -14,18 +19,13 @@ func unproject_point(f32x3 position, in mat4 inv_proj_view_mat) -> f32x3 { return p.xyz / p.w; } -struct PushConstants { - Camera *camera; -}; -[[vk::push_constant]] PushConstants C; - [[shader("vertex")]] VertexOutput vs_main(u32 vertex_id : SV_VertexID) { const let uv = f32x2((vertex_id << 1) & 2, vertex_id & 2); VertexOutput output; output.position = f32x4(uv * 2.0 - 1.0, 0.5, 1.0); - output.far_pos = unproject_point(f32x3(output.position.xy, 1.0), C.camera->inv_projection_view_mat); - output.near_pos = unproject_point(f32x3(output.position.xy, 0.0), C.camera->inv_projection_view_mat); + output.far_pos = unproject_point(f32x3(output.position.xy, 1.0), params.camera.inv_projection_view_mat); + output.near_pos = unproject_point(f32x3(output.position.xy, 0.0), params.camera.inv_projection_view_mat); return output; } @@ -70,16 +70,17 @@ struct FragmentOutput { FragmentOutput fs_main(VertexOutput input) { FragmentOutput output; float t = -input.near_pos.y / (input.far_pos.y - input.near_pos.y); - if (t < 0.0) - discard; + if (t != 0.0) { + //discard; + } float3 pixel_pos = input.near_pos + t * (input.far_pos - input.near_pos); pixel_pos.y -= 0.1; - float4 clip_space_pos = mul(C.camera->projection_view_mat, float4(pixel_pos, 1.0)); + float4 clip_space_pos = mul(params.camera.projection_view_mat, float4(pixel_pos, 1.0)); float depth = clip_space_pos.z / clip_space_pos.w; - output.color = pristine_grid(pixel_pos, float2(0.005)) * float(t > 0.0); + output.color = pristine_grid(pixel_pos, float2(0.005)); output.depth = depth; return output; } diff --git a/Lorr/Engine/Resources/shaders/passes/editor_mousepick.slang b/Lorr/Engine/Resources/shaders/passes/editor_mousepick.slang index 3caf42ee..be6ef9e6 100644 --- a/Lorr/Engine/Resources/shaders/passes/editor_mousepick.slang +++ b/Lorr/Engine/Resources/shaders/passes/editor_mousepick.slang @@ -25,7 +25,7 @@ struct PushConstants { [[shader("compute")]] [[numthreads(1, 1, 1)]] func cs_main() -> void { - const u32 texel = visbuffer_data.load(C.texel); + const u32 texel = visbuffer_data.Load(u32x3(C.texel, 0)); if (texel == ~0u) { *C.dst = ~0u; return; diff --git a/Lorr/Engine/Resources/shaders/passes/generate_cull_commands.slang b/Lorr/Engine/Resources/shaders/passes/generate_cull_commands.slang index 3495f0fe..de936bd7 100644 --- a/Lorr/Engine/Resources/shaders/passes/generate_cull_commands.slang +++ b/Lorr/Engine/Resources/shaders/passes/generate_cull_commands.slang @@ -1,17 +1,11 @@ import std; import gpu; -struct ShaderParameters { - StructuredBuffer meshlet_instances_count; - - RWStructuredBuffer cull_meshlets_cmd; -}; +[[vk::binding(0)]] StructuredBuffer visible_meshlet_instances_count; +[[vk::binding(1)]] RWStructuredBuffer cull_meshlets_cmd; [[shader("compute")]] [[numthreads(1, 1, 1)]] -func cs_main( - uniform ParameterBlock params -) -> void { - params.cull_meshlets_cmd[0].x = (params.meshlet_instances_count[0] + (CULLING_MESHLET_COUNT - 1)) / CULLING_MESHLET_COUNT; +func cs_main() -> void { + cull_meshlets_cmd[0].x = (visible_meshlet_instances_count[2] + (CULLING_MESHLET_COUNT - 1)) / CULLING_MESHLET_COUNT; } - diff --git a/Lorr/Engine/Resources/shaders/passes/histogram_average.slang b/Lorr/Engine/Resources/shaders/passes/histogram_average.slang index ecb13043..5e1475b6 100644 --- a/Lorr/Engine/Resources/shaders/passes/histogram_average.slang +++ b/Lorr/Engine/Resources/shaders/passes/histogram_average.slang @@ -29,15 +29,14 @@ func cs_main( ) -> 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); + GroupMemoryBarrierWithGroupSync(); [[unroll]] for (u32 cutoff = (HISTOGRAM_BIN_COUNT >> 1); cutoff > 0; cutoff >>= 1) { if (gid < cutoff) { histogram_shared[gid] += histogram_shared[gid + cutoff]; } - - std::control_barrier(std::memory_order_acq_rel); + GroupMemoryBarrierWithGroupSync(); } if (gid == 0) { diff --git a/Lorr/Engine/Resources/shaders/passes/histogram_generate.slang b/Lorr/Engine/Resources/shaders/passes/histogram_generate.slang index e2367161..a5857642 100644 --- a/Lorr/Engine/Resources/shaders/passes/histogram_generate.slang +++ b/Lorr/Engine/Resources/shaders/passes/histogram_generate.slang @@ -35,15 +35,15 @@ func cs_main( uniform i32x3 src_extent ) -> void { histogram_shared[group_index] = 0; - std::control_barrier(std::memory_order_acq_rel); + GroupMemoryBarrierWithGroupSync(); if (all(thread_id.xy < src_extent.xy)) { - const f32x3 color = params.src_image.load(thread_id.xy).rgb; + const f32x3 color = params.src_image.Load(u32x3(thread_id.xy, 0)).rgb; const f32 luminance = std::rec2020_to_xyz(color).y; 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); + __atomic_add(histogram_shared[bin_index], 1, MemoryOrder::AcquireRelease); } - std::control_barrier(std::memory_order_acq_rel); - std::atomic_add(params.histogram_bin_indices[group_index], histogram_shared[group_index], std::memory_order_relaxed); + GroupMemoryBarrierWithGroupSync(); + __atomic_add(params.histogram_bin_indices[group_index], histogram_shared[group_index], MemoryOrder::Relaxed); } diff --git a/Lorr/Engine/Resources/shaders/passes/hiz.slang b/Lorr/Engine/Resources/shaders/passes/hiz.slang index 2f21880d..1a01f2cb 100644 --- a/Lorr/Engine/Resources/shaders/passes/hiz.slang +++ b/Lorr/Engine/Resources/shaders/passes/hiz.slang @@ -3,10 +3,10 @@ module hiz; import std; import gpu; -// Do not remove this comment: -// Taken from: https://github.dev/SparkyPotato/radiance/blob/main/shaders/passes/mesh/hzb.slang, -// which is based on https://github.com/Themaister/Granite/blob/master/assets/shaders/post/hiz.comp, -// which is HiZ modification of AMD's Single Pass Downsampler. +// Credits: +// - https://github.dev/SparkyPotato/radiance/blob/main/shaders/passes/mesh/hzb.slang, +// - https://github.com/Themaister/Granite/blob/master/assets/shaders/post/hiz.comp, +// - AMD's Single Pass Downsampler. [[vk::binding(0)]] globallycoherent RWStructuredBuffer spd_global_atomic; @@ -20,7 +20,7 @@ typealias HiZMip = StorageImage2D; [[vk::binding(6)]] HiZMip dst_mip_3; [[vk::binding(7)]] HiZMip dst_mip_4; [[vk::binding(8)]] HiZMip dst_mip_5; -[[vk::binding(9)]] HiZMip dst_mip_6; +[[vk::binding(9)]] globallycoherent HiZMip dst_mip_6; [[vk::binding(10)]] HiZMip dst_mip_7; [[vk::binding(11)]] HiZMip dst_mip_8; [[vk::binding(12)]] HiZMip dst_mip_9; @@ -85,7 +85,7 @@ func reduce(f32x4 v) -> f32 { } func store(u32x2 texel, u32 mip, f32 v) -> void { - get_mip_image(mip).store(texel, v, mip == 6 ? MemoryScope::QueueFamily : MemoryScope::Device); + get_mip_image(mip).Store(texel, v); } func store_2x2(u32x2 p, u32 mip, f32x4 v) -> void { @@ -97,7 +97,7 @@ func store_2x2(u32x2 p, u32 mip, f32x4 v) -> void { func load(u32x2 texel) -> f32 { f32x2 uv = f32x2(texel) * inv_src_extent + inv_src_extent; - return src_image.sample_mip(sampler, uv, 0); + return src_image.SampleLevel(sampler, uv, 0); } func load_2x2(u32x2 p) -> f32x4 { @@ -121,7 +121,7 @@ func load_mid(u32x2 texel) -> f32 { texel = min(texel, src_extent >> 6); } - return dst_mip_6.load(texel, MemoryScope.QueueFamily); + return dst_mip_6.Load(texel); } func load_mid_2x2(u32x2 p) -> f32x4 { @@ -214,7 +214,7 @@ func cs_main(u32x2 group_id : SV_GroupID) -> void { shared_buffer[local_id >> 4] = d; } - std::control_barrier(std::memory_order_acq_rel); + GroupMemoryBarrierWithGroupSync(); if (local_id < 16) { d = reduce_mip_simd(group_id * 4 + p, local_id, 4, shared_buffer[local_id]); @@ -228,15 +228,13 @@ func cs_main(u32x2 group_id : SV_GroupID) -> void { return; } - std::control_barrier(std::memory_order_acq_rel, - MemoryScope::Workgroup, MemoryScope::QueueFamily, - std::MemoryLocation::Image | std::MemoryLocation::Workgroup); - + AllMemoryBarrierWithGroupSync(); + if (local_id == 0) { - is_last = std::atomic_add(spd_global_atomic[0], 1, std::memory_order_acq_rel) == C.work_group_count - 1; + is_last = __atomic_add(spd_global_atomic[0], 1, MemoryOrder::AcquireRelease) == C.work_group_count - 1; } - std::control_barrier(std::memory_order_acq_rel); + GroupMemoryBarrierWithGroupSync(); if (!is_last) { return; } @@ -259,7 +257,7 @@ func cs_main(u32x2 group_id : SV_GroupID) -> void { shared_buffer[local_id >> 4] = d; } - std::control_barrier(std::memory_order_acq_rel); + GroupMemoryBarrierWithGroupSync(); if (local_id < 16) { d = reduce_mip_simd(p, local_id, 10, shared_buffer[local_id]); diff --git a/Lorr/Engine/Resources/shaders/passes/hiz_slow.slang b/Lorr/Engine/Resources/shaders/passes/hiz_slow.slang new file mode 100644 index 00000000..8b713bd1 --- /dev/null +++ b/Lorr/Engine/Resources/shaders/passes/hiz_slow.slang @@ -0,0 +1,22 @@ +module hiz; + +import std; +import gpu; + +struct ShaderParameters { + Sampler sampler; + Image2D src_image; + StorageImage2D dst_mip; +} + +[[shader("compute")]] +[[numthreads(32, 32, 1)]] +func cs_main( + u32x2 thread_id : SV_DispatchThreadID, + uniform ParameterBlock params, + uniform u32x2 src_image_size, + uniform u32 mip_index +) -> void { + let c = params.src_image.SampleLevel(params.sampler, (f32x2(thread_id) + 0.5) / f32x2(src_image_size), mip_index).r; + params.dst_mip.Store(thread_id.xy, c); +} diff --git a/Lorr/Engine/Resources/shaders/passes/imgui.slang b/Lorr/Engine/Resources/shaders/passes/imgui.slang index 0d0eb5e9..d74fe99d 100644 --- a/Lorr/Engine/Resources/shaders/passes/imgui.slang +++ b/Lorr/Engine/Resources/shaders/passes/imgui.slang @@ -40,5 +40,5 @@ func vs_main(VertexInput input) -> VertexOutput { [[shader("fragment")]] func fs_main(VertexOutput input) -> f32x4 { - return texture.sample(sampler, input.tex_coord) * input.color; + return texture.Sample(sampler, input.tex_coord) * input.color; } diff --git a/Lorr/Engine/Resources/shaders/passes/select_lods.slang b/Lorr/Engine/Resources/shaders/passes/select_lods.slang deleted file mode 100644 index f5d9cce4..00000000 --- a/Lorr/Engine/Resources/shaders/passes/select_lods.slang +++ /dev/null @@ -1,82 +0,0 @@ -import std; -import gpu; -import scene; -import cull; -import debug_drawer; - -struct ShaderParameters { - ConstantBuffer camera; - StructuredBuffer meshes; - StructuredBuffer transforms; - - RWStructuredBuffer mesh_instances; - RWStructuredBuffer meshlet_instances; - RWStructuredBuffer meshlet_instances_count; - RWStructuredBuffer debug_drawer; -}; - -#ifndef CULLING_MESHES_COUNT - #define CULLING_MESHES_COUNT 64 -#endif - -[[shader("compute")]] -[[numthreads(CULLING_MESHES_COUNT, 1, 1)]] -func cs_main( - uint3 thread_id : SV_DispatchThreadID, - uniform ParameterBlock params, - uniform u32 mesh_instances_count, - uniform CullFlags cull_flags -) -> void { - let mesh_instance_index = thread_id.x; - if (mesh_instance_index >= mesh_instances_count) { - return; - } - - let mesh_instance = ¶ms.mesh_instances[mesh_instance_index]; - let mesh = params.meshes[mesh_instance.mesh_index]; - let transform = params.transforms[mesh_instance.transform_index]; - let mvp = mul(params.camera.projection_view_mat, transform.world); - if (!test_frustum(mvp, mesh.bounds.aabb_center, mesh.bounds.aabb_extent)) { - return; - } - - var lod_index = 0; - if (true) { - // Credits: - // - https://github.com/Sunset-Flock/Timberdoodle/blob/786f141e261dff4756e7f1a67dd7f7a5e1277956/src/scene/mesh_lod.hpp#L45 - let aabb_center = mul(transform.world, f32x4(mesh.bounds.aabb_center, 1.0)).xyz; - let aabb_extent_x = length(transform.world[0]) * mesh.bounds.aabb_extent.x; - let aabb_extent_y = length(transform.world[1]) * mesh.bounds.aabb_extent.y; - let aabb_extent_z = length(transform.world[2]) * mesh.bounds.aabb_extent.z; - let aabb_rough_extent = max(max(aabb_extent_x, aabb_extent_y), aabb_extent_z); - let aabb_rough_camera_distance = max(length(aabb_center - params.camera.position) - 0.5 * aabb_rough_extent, 0.0); - - // Avoiding the atan here - let rough_resolution = max(params.camera.resolution.x, params.camera.resolution.y); - let fov90_distance_to_screen_ratio = 2.0f; - let pixel_size_at_1m = fov90_distance_to_screen_ratio / rough_resolution; - let aabb_size_at_1m = (aabb_rough_extent / aabb_rough_camera_distance); - let rough_aabb_pixel_size = aabb_size_at_1m / pixel_size_at_1m; - - for (var i = 1; i < mesh.lod_count; i++) { - let mesh_lod = mesh.lods[i]; - let rough_pixel_error = rough_aabb_pixel_size * mesh_lod.error; - if (rough_pixel_error < params.camera.acceptable_lod_error) { - lod_index = i; - } else { - break; - } - } - } - - mesh_instance.lod_index = lod_index; - let mesh_lod = mesh.lods[lod_index]; - let meshlet_instance_offset = std::atomic_add(params.meshlet_instances_count[0], mesh_lod.meshlet_count, std::memory_order_relaxed); - for (u32 i = 0; i < mesh_lod.meshlet_count; i++) { - let offset = meshlet_instance_offset + i; - var meshlet_instance = MeshletInstance(); - meshlet_instance.mesh_instance_index = mesh_instance_index; - meshlet_instance.meshlet_index = i; - params.meshlet_instances[offset] = meshlet_instance; - } -} diff --git a/Lorr/Engine/Resources/shaders/passes/sky_aerial_perspective.slang b/Lorr/Engine/Resources/shaders/passes/sky_aerial_perspective.slang index e43a1c59..a6f4f080 100644 --- a/Lorr/Engine/Resources/shaders/passes/sky_aerial_perspective.slang +++ b/Lorr/Engine/Resources/shaders/passes/sky_aerial_perspective.slang @@ -76,14 +76,12 @@ func cs_main( info.sun_intensity = params.environment.sun_intensity; info.max_integration_length = t_max_max; info.eval_planet_luminance = false; + info.eval_multiscattering = true; info.sampling.variable_sample_count = false; info.sampling.initial_sample_count = max(1.0, (f32(thread_id.z) + 1.0) * 2.0); - info.transmittance_image = params.sky_transmittance_lut; - info.multiscattering_image = params.sky_multiscattering_lut; - - let result = integrate_single_scattered_luminance(params.environment, params.sampler, info); + let result = integrate_single_scattered_luminance(info, params.environment, params.sampler, params.sky_transmittance_lut, params.sky_multiscattering_lut); 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 0f939813..d0df5b48 100644 --- a/Lorr/Engine/Resources/shaders/passes/sky_final.slang +++ b/Lorr/Engine/Resources/shaders/passes/sky_final.slang @@ -48,7 +48,7 @@ func fs_main( VertexOutput input, uniform ParameterBlock params ) -> f32x4 { - f32 depth = params.depth_image.sample_mip(params.sampler, input.tex_coord, 0.0); + f32 depth = params.depth_image.SampleLevel(params.sampler, input.tex_coord, 0.0); f32x3 NDC = f32x3(input.tex_coord * 2.0 - 1.0, depth); 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; @@ -86,7 +86,7 @@ func fs_main( eye_altitude, view_zenith_cos_angle, light_on_plane); - f32x4 result = params.sky_view_lut.sample_mip(params.sampler, uv, 0.0); + f32x4 result = params.sky_view_lut.SampleLevel(params.sampler, uv, 0.0); f32x3 luminance = result.rgb; f32 transmittance = result.a; @@ -95,7 +95,7 @@ func fs_main( 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; + f32x3 sun_transmittance = params.sky_transmittance_lut.SampleLevel(params.sampler, transmittance_uv, 0.0).rgb; if (!planet_intersection.hasValue) { luminance += draw_sun(eye_dir, params.environment.sun_direction, 1.0) * params.environment.sun_intensity * sun_transmittance; diff --git a/Lorr/Engine/Resources/shaders/passes/sky_multiscattering.slang b/Lorr/Engine/Resources/shaders/passes/sky_multiscattering.slang index 1fbe32d3..0c227c51 100644 --- a/Lorr/Engine/Resources/shaders/passes/sky_multiscattering.slang +++ b/Lorr/Engine/Resources/shaders/passes/sky_multiscattering.slang @@ -37,23 +37,22 @@ func cs_main( info.eval_mie_phase = false; info.eval_rayleigh_phase = false; info.eval_planet_luminance = true; + info.eval_multiscattering = false; info.sampling.variable_sample_count = false; info.sampling.initial_sample_count = 32; - info.transmittance_image = params.sky_transmittance_lut; - f32x3 luminance = 0.0; 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.environment, params.sampler, info); + let result = integrate_single_scattered_luminance(info, params.environment, params.sampler, params.sky_transmittance_lut, params.sky_transmittance_lut); multi_scattering_as_1 += result.multiscattering_as_1; luminance += result.luminance; } - const let sphere_solid_angle = 4.0f * PI; - const let isotropic_phase = 1.0f / sphere_solid_angle; - const let inv_sample_count = 1.0 / f32(SAMPLE_COUNT); + let sphere_solid_angle = 4.0f * PI; + let isotropic_phase = 1.0f / sphere_solid_angle; + let inv_sample_count = 1.0 / f32(SAMPLE_COUNT); luminance *= sphere_solid_angle * inv_sample_count; multi_scattering_as_1 *= inv_sample_count; f32x3 scattered_luminance = luminance * isotropic_phase; diff --git a/Lorr/Engine/Resources/shaders/passes/sky_transmittance.slang b/Lorr/Engine/Resources/shaders/passes/sky_transmittance.slang index 4781835a..15ad0765 100644 --- a/Lorr/Engine/Resources/shaders/passes/sky_transmittance.slang +++ b/Lorr/Engine/Resources/shaders/passes/sky_transmittance.slang @@ -31,7 +31,7 @@ func cs_main( f32x3 sun_dir = f32x3(0.0, sqrt(1.0 - lut_y * lut_y), lut_y); f32x3 ray_pos = f32x3(0.0, 0.0, lut_x); - const f32 STEP_COUNT = 1000.0; + const f32 STEP_COUNT = 420.0; 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; diff --git a/Lorr/Engine/Resources/shaders/passes/sky_view.slang b/Lorr/Engine/Resources/shaders/passes/sky_view.slang index 107b6d99..87c0b097 100644 --- a/Lorr/Engine/Resources/shaders/passes/sky_view.slang +++ b/Lorr/Engine/Resources/shaders/passes/sky_view.slang @@ -31,7 +31,7 @@ func cs_main( 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); + params.sky_view_lut.Store(thread_id.xy, 0.0); return; } @@ -49,12 +49,9 @@ func cs_main( info.sampling.variable_sample_count = true; info.sampling.min_sample_count = sample_count; info.sampling.max_sample_count = sample_count; - - info.transmittance_image = params.sky_transmittance_lut; - info.multiscattering_image = params.sky_multiscattering_lut; - - let result = integrate_single_scattered_luminance(params.environment, params.sampler, info); + info.eval_multiscattering = true; + let result = integrate_single_scattered_luminance(info, params.environment, params.sampler, params.sky_transmittance_lut, params.sky_multiscattering_lut); let transmittance = dot(result.transmittance, 1.0 / 3.0); - params.sky_view_lut.store(thread_id.xy, f32x4(result.luminance, transmittance)); + 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 49f6afe7..13900b34 100644 --- a/Lorr/Engine/Resources/shaders/passes/tonemap.slang +++ b/Lorr/Engine/Resources/shaders/passes/tonemap.slang @@ -146,9 +146,413 @@ float3 agx_tonemapping(float3 color) { return color; } +// ----------------------------------------------------------------------------- +// Mode options. +// ----------------------------------------------------------------------------- +#define TONE_MAPPING_UCS_ICTCP 0 +#define TONE_MAPPING_UCS_JZAZBZ 1 +#define TONE_MAPPING_UCS TONE_MAPPING_UCS_ICTCP + +// ----------------------------------------------------------------------------- +// Defines the SDR reference white level used in our tone mapping (typically 250 nits). +// ----------------------------------------------------------------------------- +#define GRAN_TURISMO_SDR_PAPER_WHITE 250.0f // cd/m^2 + +// ----------------------------------------------------------------------------- +// Gran Turismo luminance-scale conversion helpers. +// In Gran Turismo, 1.0f in the linear frame-buffer space corresponds to +// REFERENCE_LUMINANCE cd/m^2 of physical luminance (typically 100 cd/m^2). +// ----------------------------------------------------------------------------- +#define REFERENCE_LUMINANCE 100.0f // cd/m^2 <-> 1.0f + +float +frameBufferValueToPhysicalValue(float fbValue) +{ + // Converts linear frame-buffer value to physical luminance (cd/m^2) + // where 1.0 corresponds to REFERENCE_LUMINANCE (e.g., 100 cd/m^2). + return fbValue * REFERENCE_LUMINANCE; +} + +float +physicalValueToFrameBufferValue(float physical) +{ + // Converts physical luminance (cd/m^2) to a linear frame-buffer value, + // where 1.0 corresponds to REFERENCE_LUMINANCE (e.g., 100 cd/m^2). + return physical / REFERENCE_LUMINANCE; +} + +// ----------------------------------------------------------------------------- +// Utility functions. +// ----------------------------------------------------------------------------- +float +smoothStep(float x, float edge0, float edge1) +{ + float t = (x - edge0) / (edge1 - edge0); + + if (x < edge0) + { + return 0.0f; + } + if (x > edge1) + { + return 1.0f; + } + + return t * t * (3.0f - 2.0f * t); +} + +float +chromaCurve(float x, float a, float b) +{ + return 1.0f - smoothStep(x, a, b); +} + + +// ----------------------------------------------------------------------------- +// "GT Tone Mapping" curve with convergent shoulder. +// ----------------------------------------------------------------------------- +struct GTToneMappingCurveV2 +{ + float peakIntensity_; + float alpha_; + float midPoint_; + float linearSection_; + float toeStrength_; + float kA_, kB_, kC_; + + [mutating] + void initializeCurve(float monitorIntensity, + float alpha, + float grayPoint, + float linearSection, + float toeStrength) + { + peakIntensity_ = monitorIntensity; + alpha_ = alpha; + midPoint_ = grayPoint; + linearSection_ = linearSection; + toeStrength_ = toeStrength; + + // Pre-compute constants for the shoulder region. + float k = (linearSection_ - 1.0f) / (alpha_ - 1.0f); + kA_ = peakIntensity_ * linearSection_ + peakIntensity_ * k; + kB_ = -peakIntensity_ * k * exp(linearSection_ / k); + kC_ = -1.0f / (k * peakIntensity_); + } + + float evaluateCurve(float x) + { + if (x < 0.0f) + { + return 0.0f; + } + + float weightLinear = smoothStep(x, 0.0f, midPoint_); + float weightToe = 1.0f - weightLinear; + + // Shoulder mapping for highlights. + float shoulder = kA_ + kB_ * exp(x * kC_); + + if (x < linearSection_ * peakIntensity_) + { + float toeMapped = midPoint_ * pow(x / midPoint_, toeStrength_); + return weightToe * toeMapped + weightLinear * x; + } + else + { + return shoulder; + } + } +}; + +// ----------------------------------------------------------------------------- +// EOTF / inverse-EOTF for ST-2084 (PQ). +// Note: Introduce exponentScaleFactor to allow scaling of the exponent in the EOTF for Jzazbz. +// ----------------------------------------------------------------------------- +float +eotfSt2084(float n, float exponentScaleFactor = 1.0f) +{ + if (n < 0.0f) + { + n = 0.0f; + } + if (n > 1.0f) + { + n = 1.0f; + } + + // Base functions from SMPTE ST 2084:2014 + // Converts from normalized PQ (0-1) to absolute luminance in cd/m^2 (linear light) + // Assumes float input; does not handle integer encoding (Annex) + // Assumes full-range signal (0-1) + const float m1 = 0.1593017578125f; // (2610 / 4096) / 4 + const float m2 = 78.84375f * exponentScaleFactor; // (2523 / 4096) * 128 + const float c1 = 0.8359375f; // 3424 / 4096 + const float c2 = 18.8515625f; // (2413 / 4096) * 32 + const float c3 = 18.6875f; // (2392 / 4096) * 32 + const float pqC = 10000.0f; // Maximum luminance supported by PQ (cd/m^2) + + // Does not handle signal range from 2084 - assumes full range (0-1) + float np = pow(n, 1.0f / m2); + float l = np - c1; + + if (l < 0.0f) + { + l = 0.0f; + } + + l = l / (c2 - c3 * np); + l = pow(l, 1.0f / m1); + + // Convert absolute luminance (cd/m^2) into the frame-buffer linear scale. + return physicalValueToFrameBufferValue(l * pqC); +} + +float +inverseEotfSt2084(float v, float exponentScaleFactor = 1.0f) +{ + const float m1 = 0.1593017578125f; + const float m2 = 78.84375f * exponentScaleFactor; + const float c1 = 0.8359375f; + const float c2 = 18.8515625f; + const float c3 = 18.6875f; + const float pqC = 10000.0f; + + // Convert the frame-buffer linear scale into absolute luminance (cd/m^2). + float physical = frameBufferValueToPhysicalValue(v); + float y = physical / pqC; // Normalize for the ST-2084 curve + + float ym = pow(y, m1); + return exp2(m2 * (log2(c1 + c2 * ym) - log2(1.0f + c3 * ym))); +} + +// ----------------------------------------------------------------------------- +// ICtCp conversion. +// Reference: ITU-T T.302 (https://www.itu.int/rec/T-REC-T.302/en) +// ----------------------------------------------------------------------------- +void +rgbToICtCp(f32x3 rgb, out f32x3 ictCp) // Input: linear Rec.2020 +{ + float l = (rgb[0] * 1688.0f + rgb[1] * 2146.0f + rgb[2] * 262.0f) / 4096.0f; + float m = (rgb[0] * 683.0f + rgb[1] * 2951.0f + rgb[2] * 462.0f) / 4096.0f; + float s = (rgb[0] * 99.0f + rgb[1] * 309.0f + rgb[2] * 3688.0f) / 4096.0f; + + float lPQ = inverseEotfSt2084(l); + float mPQ = inverseEotfSt2084(m); + float sPQ = inverseEotfSt2084(s); + + ictCp[0] = (2048.0f * lPQ + 2048.0f * mPQ) / 4096.0f; + ictCp[1] = (6610.0f * lPQ - 13613.0f * mPQ + 7003.0f * sPQ) / 4096.0f; + ictCp[2] = (17933.0f * lPQ - 17390.0f * mPQ - 543.0f * sPQ) / 4096.0f; +} + +void +iCtCpToRgb(f32x3 ictCp, out f32x3 rgb) // Output: linear Rec.2020 +{ + float l = ictCp[0] + 0.00860904f * ictCp[1] + 0.11103f * ictCp[2]; + float m = ictCp[0] - 0.00860904f * ictCp[1] - 0.11103f * ictCp[2]; + float s = ictCp[0] + 0.560031f * ictCp[1] - 0.320627f * ictCp[2]; + + float lLin = eotfSt2084(l); + float mLin = eotfSt2084(m); + float sLin = eotfSt2084(s); + + rgb[0] = max(3.43661f * lLin - 2.50645f * mLin + 0.0698454f * sLin, 0.0f); + rgb[1] = max(-0.79133f * lLin + 1.9836f * mLin - 0.192271f * sLin, 0.0f); + rgb[2] = max(-0.0259499f * lLin - 0.0989137f * mLin + 1.12486f * sLin, 0.0f); +} + +// ----------------------------------------------------------------------------- +// Jzazbz conversion. +// Reference: +// Muhammad Safdar, Guihua Cui, Youn Jin Kim, and Ming Ronnier Luo, +// "Perceptually uniform color space for image signals including high dynamic +// range and wide gamut," Opt. Express 25, 15131-15151 (2017) +// Note: Coefficients adjusted for linear Rec.2020 +// ----------------------------------------------------------------------------- +#define JZAZBZ_EXPONENT_SCALE_FACTOR 1.7f // Scale factor for exponent + +void +rgbToJzazbz(f32x3 rgb, out f32x3 jab) // Input: linear Rec.2020 +{ + float l = rgb[0] * 0.530004f + rgb[1] * 0.355704f + rgb[2] * 0.086090f; + float m = rgb[0] * 0.289388f + rgb[1] * 0.525395f + rgb[2] * 0.157481f; + float s = rgb[0] * 0.091098f + rgb[1] * 0.147588f + rgb[2] * 0.734234f; + + float lPQ = inverseEotfSt2084(l, JZAZBZ_EXPONENT_SCALE_FACTOR); + float mPQ = inverseEotfSt2084(m, JZAZBZ_EXPONENT_SCALE_FACTOR); + float sPQ = inverseEotfSt2084(s, JZAZBZ_EXPONENT_SCALE_FACTOR); + + float iz = 0.5f * lPQ + 0.5f * mPQ; + + jab[0] = (0.44f * iz) / (1.0f - 0.56f * iz) - 1.6295499532821566e-11f; + jab[1] = 3.524000f * lPQ - 4.066708f * mPQ + 0.542708f * sPQ; + jab[2] = 0.199076f * lPQ + 1.096799f * mPQ - 1.295875f * sPQ; +} + +void +jzazbzToRgb(f32x3 jab, out f32x3 rgb) // Output: linear Rec.2020 +{ + float jz = jab[0] + 1.6295499532821566e-11f; + float iz = jz / (0.44f + 0.56f * jz); + float a = jab[1]; + float b = jab[2]; + + float l = iz + a * 1.386050432715393e-1f + b * 5.804731615611869e-2f; + float m = iz + a * -1.386050432715393e-1f + b * -5.804731615611869e-2f; + float s = iz + a * -9.601924202631895e-2f + b * -8.118918960560390e-1f; + + float lLin = eotfSt2084(l, JZAZBZ_EXPONENT_SCALE_FACTOR); + float mLin = eotfSt2084(m, JZAZBZ_EXPONENT_SCALE_FACTOR); + float sLin = eotfSt2084(s, JZAZBZ_EXPONENT_SCALE_FACTOR); + + rgb[0] = lLin * 2.990669f + mLin * -2.049742f + sLin * 0.088977f; + rgb[1] = lLin * -1.634525f + mLin * 3.145627f + sLin * -0.483037f; + rgb[2] = lLin * -0.042505f + mLin * -0.377983f + sLin * 1.448019f; +} + +// ----------------------------------------------------------------------------- +// Unified color space (UCS): ICtCp or Jzazbz. +// ----------------------------------------------------------------------------- +#if TONE_MAPPING_UCS == TONE_MAPPING_UCS_ICTCP +void +rgbToUcs(f32x3 rgb, out f32x3 ucs) +{ + rgbToICtCp(rgb, ucs); +} +void +ucsToRgb(f32x3 ucs, out f32x3 rgb) +{ + iCtCpToRgb(ucs, rgb); +} +#elif TONE_MAPPING_UCS == TONE_MAPPING_UCS_JZAZBZ +void +rgbToUcs(f32x3 rgb, out f32x3 ucs) +{ + rgbToJzazbz(rgb, ucs); +} +void +ucsToRgb(f32x3 ucs, out f32x3 rgb) +{ + jzazbzToRgb(ucs, rgb); +} +#else +#error "Unsupported TONE_MAPPING_UCS value. Please define TONE_MAPPING_UCS as either TONE_MAPPING_UCS_ICTCP or TONE_MAPPING_UCS_JZAZBZ." +#endif + +// ----------------------------------------------------------------------------- +// GT7 Tone Mapping class. +// ----------------------------------------------------------------------------- +struct GT7ToneMapping +{ + float sdrCorrectionFactor_; + + float framebufferLuminanceTarget_; + float framebufferLuminanceTargetUcs_; // Target luminance in UCS space + GTToneMappingCurveV2 curve_; + + float blendRatio_; + float fadeStart_; + float fadeEnd_; + + // Initializes the tone mapping curve and related parameters based on the target display luminance. + // This method should not be called directly. Use initializeAsHDR() or initializeAsSDR() instead. + [mutating] + void initializeParameters(float physicalTargetLuminance) + { + framebufferLuminanceTarget_ = physicalValueToFrameBufferValue(physicalTargetLuminance); + + // Initialize the curve (slightly different parameters from GT Sport). + curve_.initializeCurve(framebufferLuminanceTarget_, 0.25f, 0.538f, 0.444f, 1.280f); + + // Default parameters. + blendRatio_ = 0.6f; + fadeStart_ = 0.98f; + fadeEnd_ = 1.16f; + + f32x3 ucs; + f32x3 rgb = { framebufferLuminanceTarget_, + framebufferLuminanceTarget_, + framebufferLuminanceTarget_ }; + rgbToUcs(rgb, ucs); + framebufferLuminanceTargetUcs_ = + ucs[0]; // Use the first UCS component (I or Jz) as luminance + } + + // Initialize for HDR (High Dynamic Range) display. + // Input: target display peak luminance in nits (range: 250 to 10,000) + // Note: The lower limit is 250 because the parameters for GTToneMappingCurveV2 + // were determined based on an SDR paper white assumption of 250 nits (GRAN_TURISMO_SDR_PAPER_WHITE). + [mutating] + void initializeAsHDR(float physicalTargetLuminance) + { + sdrCorrectionFactor_ = 1.0f; + initializeParameters(physicalTargetLuminance); + } + + // Initialize for SDR (Standard Dynamic Range) display. + [mutating] + void initializeAsSDR() + { + // Regarding SDR output: + // First, in GT (Gran Turismo), it is assumed that a maximum value of 1.0 in SDR output + // corresponds to GRAN_TURISMO_SDR_PAPER_WHITE (typically 250 nits). + // Therefore, tone mapping for SDR output is performed based on GRAN_TURISMO_SDR_PAPER_WHITE. + // However, in the sRGB standard, 1.0f corresponds to 100 nits, + // so we need to "undo" the tone-mapped values accordingly. + // To match the sRGB range, the tone-mapped values are scaled using sdrCorrectionFactor_. + // + // * These adjustments ensure that the visual appearance (in terms of brightness) + // stays generally consistent across both HDR and SDR outputs for the same rendered content. + sdrCorrectionFactor_ = 1.0f / physicalValueToFrameBufferValue(GRAN_TURISMO_SDR_PAPER_WHITE); + initializeParameters(GRAN_TURISMO_SDR_PAPER_WHITE); + } + + // Input: linear Rec.2020 RGB (frame buffer values) + // Output: tone-mapped RGB (frame buffer values); + // - in SDR mode: mapped to [0, 1], ready for sRGB OETF + // - in HDR mode: mapped to [0, framebufferLuminanceTarget_], ready for PQ inverse-EOTF + // Note: framebufferLuminanceTarget_ represents the display's target peak luminance converted to a frame buffer value. + // The returned values are suitable for applying the appropriate OETF to generate final output signal. + void applyToneMapping(f32x3 rgb, out f32x3 out) + { + // Convert to UCS to separate luminance and chroma. + f32x3 ucs; + rgbToUcs(rgb, ucs); + + // Per-channel tone mapping ("skewed" color). + f32x3 skewedRgb = { curve_.evaluateCurve(rgb[0]), + curve_.evaluateCurve(rgb[1]), + curve_.evaluateCurve(rgb[2]) }; + + f32x3 skewedUcs; + rgbToUcs(skewedRgb, skewedUcs); + + float chromaScale = + chromaCurve(ucs[0] / framebufferLuminanceTargetUcs_, fadeStart_, fadeEnd_); + + const f32x3 scaledUcs = { skewedUcs[0], // Luminance from skewed color + ucs[1] * chromaScale, // Scaled chroma components + ucs[2] * chromaScale }; + + // Convert back to RGB. + f32x3 scaledRgb; + ucsToRgb(scaledUcs, scaledRgb); + + // Final blend between per-channel and UCS-scaled results. + for (int i = 0; i < 3; ++i) + { + float blended = (1.0f - blendRatio_) * skewedRgb[i] + blendRatio_ * scaledRgb[i]; + // When using SDR, apply the correction factor. + // When using HDR, sdrCorrectionFactor_ is 1.0f, so it has no effect. + out[i] = sdrCorrectionFactor_ * min(blended, framebufferLuminanceTarget_); + } + } +}; + [[shader("fragment")]] f32x4 fs_main(VertexOutput input) { - f32x3 color = params.input_image.sample_mip(params.sampler, input.tex_coord, 0.0).rgb; + f32x3 color = params.input_image.SampleLevel(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); @@ -158,6 +562,10 @@ f32x4 fs_main(VertexOutput input) { //color = ACES_Fitted(color); //color = PBRNeutralToneMapping(color); // this looks like shit, figure out why color = agx_tonemapping(color); + //GT7ToneMapping gt7; + //gt7.initializeAsSDR(); + //f32x3 gt7_color; + //gt7.applyToneMapping(color, gt7_color); return f32x4(color, 1.0); } diff --git a/Lorr/Engine/Resources/shaders/passes/visbuffer_decode.slang b/Lorr/Engine/Resources/shaders/passes/visbuffer_decode.slang index 63765fc4..e1453bda 100644 --- a/Lorr/Engine/Resources/shaders/passes/visbuffer_decode.slang +++ b/Lorr/Engine/Resources/shaders/passes/visbuffer_decode.slang @@ -89,11 +89,12 @@ func compute_partial_derivatives(in f32x4x3 world_positions, in f32x2 uv, in f32 [[shader("fragment")]] func fs_main(VertexOutput input) -> FragmentOutput { - let texel = params.visbuffer.load(u32x2(input.position.xy)); + let texel = params.visbuffer.Load(u32x3(u32x2(input.position.xy), 0)); if (texel == ~0u) { discard; } + FragmentOutput output = {}; let vis = VisBufferData(texel); let meshlet_instance_index = vis.meshlet_instance_index; let meshlet_instance = params.meshlet_instances[meshlet_instance_index]; @@ -105,6 +106,11 @@ func fs_main(VertexOutput input) -> FragmentOutput { let meshlet = mesh_lod.meshlets[meshlet_instance.meshlet_index]; let indices = meshlet.indices(mesh_lod, vis.triangle_index); + if (any(indices > (mesh.vertex_count - 1))) { + // we are somehow OOB'ing + return output; + } + let positions = meshlet.positions(mesh, indices); let normals = meshlet.normals(mesh, indices); let tex_coords = meshlet.tex_coords(mesh, indices); @@ -113,8 +119,6 @@ func fs_main(VertexOutput input) -> FragmentOutput { let deriv = compute_partial_derivatives(world_positions, NDC.xy, params.camera.resolution); let tex_coord_grad = deriv.gradient_of(tex_coords); - FragmentOutput output = {}; - // ALBEDO ─────────────────────────────────────────────────────────── output.albedo_color = material.sample_albedo_color(tex_coord_grad); diff --git a/Lorr/Engine/Resources/shaders/passes/visbuffer_encode.slang b/Lorr/Engine/Resources/shaders/passes/visbuffer_encode.slang index d4970a31..29315b79 100644 --- a/Lorr/Engine/Resources/shaders/passes/visbuffer_encode.slang +++ b/Lorr/Engine/Resources/shaders/passes/visbuffer_encode.slang @@ -70,7 +70,7 @@ func fs_main(VertexOutput input) -> u32 { } #endif - std::atomic_add(params.overdraw[u32x2(input.position.xy)], 1u, std::memory_order_acq_rel, std::MemoryLocation::Image, MemoryScope::QueueFamily); + __atomic_add(params.overdraw[u32x2(input.position.xy)], 1u, MemoryOrder::AcquireRelease); let vis = VisBufferData(input.meshlet_instance_index, input.triangle_index); return vis.encode(); diff --git a/Lorr/Engine/Resources/shaders/passes/visualize_overdraw.slang b/Lorr/Engine/Resources/shaders/passes/visualize_overdraw.slang new file mode 100644 index 00000000..d28fa3a4 --- /dev/null +++ b/Lorr/Engine/Resources/shaders/passes/visualize_overdraw.slang @@ -0,0 +1,33 @@ +import gpu; + +#include + +struct ShaderParameters { + Image2D overdraw; +}; + +func inferno(f32 t) -> f32x3 { + let c0 = f32x3(0.0002189403691192265, 0.001651004631001012, -0.01948089843709184); + let c1 = f32x3(0.1065134194856116, 0.5639564367884091, 3.932712388889277); + let c2 = f32x3(11.60249308247187, -3.972853965665698, -15.9423941062914); + let c3 = f32x3(-41.70399613139459, 17.43639888205313, 44.35414519872813); + let c4 = f32x3(77.162935699427, -33.40235894210092, -81.80730925738993); + let c5 = f32x3(-71.31942824499214, 32.62606426397723, 73.20951985803202); + let c6 = f32x3(25.13112622477341, -12.24266895238567, -23.07032500287172); + + t = saturate(t); + return c0 + t * (c1 + t * (c2 + t * (c3 + t * (c4 + t * (c5 + t * c6))))); +} + +[[shader("fragment")]] +func fs_main( + VertexOutput input, + uniform ShaderParameters params, + uniform f32 heatmap_scale +) -> f32x4 { + let draw_scale = clamp(heatmap_scale, 0.0, 100.0) / 100.0; + let draw_count = f32(params.overdraw.Load(i32x3(i32x2(input.position.xy), 0))); + let heat = 1.0 - exp2(-draw_count * draw_scale); + let color = inferno(heat); + return f32x4(color, 1.0); +} \ No newline at end of file diff --git a/Lorr/Engine/Resources/shaders/pbr.slang b/Lorr/Engine/Resources/shaders/pbr.slang index 72575bf3..986ad988 100644 --- a/Lorr/Engine/Resources/shaders/pbr.slang +++ b/Lorr/Engine/Resources/shaders/pbr.slang @@ -45,22 +45,22 @@ public func BRDF(f32x3 V, f32x3 N, f32x3 L, f32x3 albedo, f32 roughness, f32 met f32 NoH = max(dot(N, H), 0.0); f32 LoH = max(dot(L, H), 0.0); - const f32 reflectance = 0.5; - f32x3 F0 = 0.16 * reflectance * reflectance * (1.0 - metallic) + albedo * metallic; + let reflectance = 0.04; + let F0 = 0.16 * reflectance * reflectance * (1.0 - metallic) + albedo * metallic; // Microfacet - f32 roughness2 = roughness * roughness; - f32 D = D_GGX(NoH, roughness2); - f32x3 G2 = Smith_G2_Height_Correlated_GGX_Lagarde(NoV, NoL, roughness2); - f32x3 F = F_Schlick(LoH, F0); - - f32x3 comp = GGX_energy_compensation(NoV, roughness2, F0); + let roughness2 = roughness * roughness; + let D = D_GGX(NoH, roughness2); + let G2 = Smith_G2_Height_Correlated_GGX_Lagarde(NoV, NoL, roughness2); + let F = F_Schlick(LoH, F0); + let J = GGX_energy_compensation(NoV, roughness2, F0); // Reflectance (Cook-Torrance) // V already divided by denominator - f32x3 specular = F * D * G2; + let specular = F * D * G2; // Diffuse - f32x3 diffuse = (1.0 - metallic) * albedo * Fd_Lambert(); + let kd = (1.0 - metallic) * (1.0 - F); + let diffuse = kd * albedo * Fd_Lambert(); // Frensel combination - return diffuse + specular * comp; + return (diffuse + specular) * J; } diff --git a/Lorr/Engine/Resources/shaders/scene.slang b/Lorr/Engine/Resources/shaders/scene.slang index 9a936be5..120838ed 100644 --- a/Lorr/Engine/Resources/shaders/scene.slang +++ b/Lorr/Engine/Resources/shaders/scene.slang @@ -3,6 +3,8 @@ 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; @@ -22,10 +24,12 @@ public enum DebugView : i32 { [[Flags]] public enum CullFlags : u32 { + MeshFrustum, + MeshOcclusion, MeshletFrustum, + MeshletOcclusion, TriangleBackFace, MicroTriangles, - Occlusion, }; [[Flags]] @@ -73,7 +77,7 @@ public struct Camera { public mat4 projection_view_mat; public mat4 inv_view_mat; public mat4 inv_projection_view_mat; - public mat4 frustum_projection_view_mat; + public mat4 prev_projection_view_mat; public f32x3 position; public f32 near_clip; public f32 far_clip; @@ -154,7 +158,7 @@ public struct Material { public func sample_albedo_color(in UVGradient grad) -> f32x4 { if (this.flags & MaterialFlag::HasAlbedoImage) { let color = bindless_images[this.albedo_image_index] - .sample_grad(bindless_samplers[this.sampler_index], grad.uv, grad.ddx, grad.ddy); + .SampleGrad(bindless_samplers[this.sampler_index], grad.uv, grad.ddx, grad.ddy); return this.albedo_color * color; } @@ -163,13 +167,13 @@ public struct Material { public func sample_normal_color(in UVGradient grad) -> f32x3 { return bindless_images[this.normal_image_index] - .sample_grad(bindless_samplers[this.sampler_index], grad.uv, grad.ddx, grad.ddy).rgb; + .SampleGrad(bindless_samplers[this.sampler_index], grad.uv, grad.ddx, grad.ddy).rgb; } public func sample_emissive_color(in UVGradient grad) -> f32x3 { if (this.flags & MaterialFlag::HasEmissiveImage) { let color = bindless_images[this.emissive_image_index] - .sample_grad(bindless_samplers[this.sampler_index], grad.uv, grad.ddx, grad.ddy).rgb; + .SampleGrad(bindless_samplers[this.sampler_index], grad.uv, grad.ddx, grad.ddy).rgb; return this.emissive_color * color; } @@ -180,7 +184,7 @@ public struct Material { let metallic_roughness = f32x2(this.metallic_factor, this.roughness_factor); if (this.flags & MaterialFlag::HasMetallicRoughnessImage) { let color = bindless_images[this.metallic_roughness_image_index] - .sample_grad(bindless_samplers[this.sampler_index], grad.uv, grad.ddx, grad.ddy).bg; + .SampleGrad(bindless_samplers[this.sampler_index], grad.uv, grad.ddx, grad.ddy).bg; return metallic_roughness * color; } @@ -190,7 +194,7 @@ public struct Material { public func sample_occlusion_color(in UVGradient grad) -> f32 { if (this.flags & MaterialFlag::HasOcclusionImage) { return bindless_images[this.occlusion_image_index] - .sample_grad(bindless_samplers[this.sampler_index], grad.uv, grad.ddx, grad.ddy).r; + .SampleGrad(bindless_samplers[this.sampler_index], grad.uv, grad.ddx, grad.ddy).r; } return 1.0; @@ -276,6 +280,7 @@ public struct MeshInstance { public u32 lod_index = 0; public u32 material_index = 0; public u32 transform_index = 0; + public u32 meshlet_instance_visibility_offset = 0; }; public struct MeshLOD { @@ -300,7 +305,7 @@ public struct Mesh { public f32x3 *vertex_positions = nullptr; public f32x3 *vertex_normals = nullptr; public f32x2 *texture_coords = nullptr; - public u32 _padding = 0; + public u32 vertex_count = 0; public u32 lod_count = 0; public MeshLOD lods[MESH_MAX_LODS] = {}; public Bounds bounds = {}; diff --git a/Lorr/Engine/Resources/shaders/sky.slang b/Lorr/Engine/Resources/shaders/sky.slang index 6c65e443..a41a58ff 100644 --- a/Lorr/Engine/Resources/shaders/sky.slang +++ b/Lorr/Engine/Resources/shaders/sky.slang @@ -172,15 +172,15 @@ public struct AtmosphereIntegrateInfo { public constexpr bool eval_mie_phase = true; public constexpr bool eval_rayleigh_phase = true; public constexpr bool eval_planet_luminance = false; - - public Image2D transmittance_image = {}; - public Optional> multiscattering_image = none; + public constexpr bool eval_multiscattering = false; }; public func integrate_single_scattered_luminance( + in AtmosphereIntegrateInfo info, in Environment environment, in Sampler lut_sampler, - in AtmosphereIntegrateInfo info + in Image2D transmittance_image = {}, + in Image2D multiscattering_image = {} ) -> AtmosphereLuminance { AtmosphereLuminance result = {}; @@ -242,20 +242,18 @@ public func integrate_single_scattered_luminance( 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, environment.atmos_planet_radius).hasValue ? 0.0 : 1.0; + f32 earth_shadow = std::ray_sphere_intersect_nearest(step_pos, 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( 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 sun_transmittance = transmittance_image.SampleLevel(lut_sampler, transmittance_uv, 0.0).rgb; f32x3 MS = 0.0; - if (info.multiscattering_image.hasValue) { + if (info.eval_multiscattering) { 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; + MS = multiscattering_image.SampleLevel(lut_sampler, multiscatter_uv, 0.0).rgb; } f32x3 scattering_phase = 0.0; @@ -275,6 +273,10 @@ public func integrate_single_scattered_luminance( f32x3 integral = (sun_luminance - sun_luminance * step_transmittance) / medium_info.extinction_sum; f32x3 ms_integral = (medium_info.scattering_sum - medium_info.scattering_sum * step_transmittance) / medium_info.extinction_sum; + let extinction_zero = medium_info.extinction_sum == f32x3(0.0); + integral = select(extinction_zero, f32x3(0.0), integral); + ms_integral = select(extinction_zero, f32x3(0.0), ms_integral); + result.luminance += info.sun_intensity * (integral * result.transmittance); result.multiscattering_as_1 += ms_integral * result.transmittance; result.transmittance *= step_transmittance; @@ -289,7 +291,7 @@ public func integrate_single_scattered_luminance( f32 NoL = saturate(dot(normalize(info.sun_dir), normalize(up_vec))); 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 sun_transmittance = transmittance_image.SampleLevel(lut_sampler, transmittance_uv, 0.0).rgb; result.luminance += info.sun_intensity * (sun_transmittance * result.transmittance * NoL * environment.atmos_terrain_albedo / PI); } @@ -327,7 +329,7 @@ public func sample_aerial_perspective( #if 0 return f32x4(relative_depth, linear_slice, non_linear_w, linear_w); #endif - f32x4 aerial_perspective = aerial_perspective_lut.sample_mip(sampler, f32x3(uv, non_linear_w), 0.0); + f32x4 aerial_perspective = aerial_perspective_lut.SampleLevel(sampler, f32x3(uv, non_linear_w), 0.0); aerial_perspective.xyz *= weight; aerial_perspective.w = 1.0 - (weight * (1.0 - aerial_perspective.w)); diff --git a/Lorr/Engine/Resources/shaders/std.slang b/Lorr/Engine/Resources/shaders/std.slang index 12242b4d..2a5a083e 100644 --- a/Lorr/Engine/Resources/shaders/std.slang +++ b/Lorr/Engine/Resources/shaders/std.slang @@ -8,7 +8,6 @@ module std; __include std.stdint; // std namespace -__include std.atomic; __include std.color; __include std.encoding; __include std.math; diff --git a/Lorr/Engine/Resources/shaders/std/atomic.slang b/Lorr/Engine/Resources/shaders/std/atomic.slang deleted file mode 100644 index 8be28137..00000000 --- a/Lorr/Engine/Resources/shaders/std/atomic.slang +++ /dev/null @@ -1,462 +0,0 @@ -implementing std; - -public namespace std { -// Can support up to SPIR-V 1.5 -public enum MemoryOrder : u32 { - Relaxed = 0x0, - Acquire = 0x2, - Release = 0x4, - AcqRel = 0x8, - SeqCst = 0x10, -}; -public static constexpr MemoryOrder memory_order_relaxed = MemoryOrder::Relaxed; -public static constexpr MemoryOrder memory_order_acquire = MemoryOrder::Acquire; -public static constexpr MemoryOrder memory_order_release = MemoryOrder::Release; -public static constexpr MemoryOrder memory_order_acq_rel = MemoryOrder::AcqRel; -public static constexpr MemoryOrder memory_order_seq_cst = MemoryOrder::SeqCst; - -public enum MemoryLocation : u32 { - None = 0, - Buffer = 0x40, - Subgroup = 0x80, - Workgroup = 0x100, - Image = 0x800, -}; - -func spirv_type_checks() -> void { - if (__type_equals()) { - spirv_asm { - OpExtension "SPV_EXT_shader_atomic_float_add"; - OpCapability AtomicFloat32MinMaxEXT - }; - } else if (__type_equals()) { - spirv_asm { - OpExtension "SPV_EXT_shader_atomic_float_add"; - OpCapability AtomicFloat16MinMaxEXT - }; - } else if (__type_equals()) { - spirv_asm { - OpExtension "SPV_EXT_shader_atomic_float_add"; - OpCapability AtomicFloat64MinMaxEXT - }; - } else if (__type_equals() || - __type_equals()) { - spirv_asm { - OpCapability Int64Atomics - }; - } -} - -[[ForceInline]] -public func atomic_load( - __ref T dst, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup -) -> T { - spirv_type_checks(); - const u32 semantics = memory_order | location; - return spirv_asm { - result:$$T = OpAtomicLoad &dst $scope $semantics; - }; -} - -[[ForceInline]] -public func atomic_store( - __ref T dst, - T desired, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup -) -> void { - spirv_type_checks(); - const u32 semantics = memory_order | location; - spirv_asm { - OpAtomicStore &dst $scope $semantics $desired; - }; -} - -[[ForceInline]] -public func atomic_increment( - __ref T dst, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup -) -> T { - spirv_type_checks(); - const u32 semantics = memory_order | location; - return spirv_asm { - result:$$T = OpAtomicIIncrement &dst $scope $semantics; - }; -} - -[[ForceInline]] -public func atomic_decrement( - __ref T dst, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup -) -> T { - spirv_type_checks(); - const u32 semantics = memory_order | location; - return spirv_asm { - result:$$T = OpAtomicIDecrement &dst $scope $semantics; - }; -} - -[[ForceInline]] -public func atomic_add( - __ref T dst, - T value, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup -) -> T { - spirv_type_checks(); - constexpr u32 semantics = memory_order | location; - return spirv_asm { - result:$$T = OpAtomicIAdd &dst $scope $semantics $value; - }; -} - -[[ForceInline]] -public func atomic_sub( - __ref T dst, - T value, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup -) -> T { - spirv_type_checks(); - constexpr u32 semantics = memory_order | location; - return spirv_asm { - result:$$T = OpAtomicISub &dst $scope $semantics $value; - }; -} - -[[ForceInline]] -public func atomic_max( - __ref T dst, - T value, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup -) -> T { - spirv_type_checks(); - const u32 semantics = memory_order | location; - if (__isUnsignedInt()) { - return spirv_asm { - result:$$T = OpAtomicUMax &dst $scope $semantics $value; - }; - } else if (__isSignedInt()) { - return spirv_asm { - result:$$T = OpAtomicSMax &dst $scope $semantics $value; - }; - } else { - spirv_asm { "" }; - } - - return {}; -} - -[[ForceInline]] -public func atomic_min( - __ref T dst, - T value, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup -) -> T { - spirv_type_checks(); - const u32 semantics = memory_order | location; - if (__isUnsignedInt()) { - return spirv_asm { - result:$$T = OpAtomicUMin &dst $scope $semantics $value; - }; - } else if (__isSignedInt()) { - return spirv_asm { - result:$$T = OpAtomicSMin &dst $scope $semantics $value; - }; - } else { - spirv_asm { "" }; - } - - return {}; -} - -[[ForceInline]] -public func atomic_and( - __ref T dst, - T value, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup -) -> T { - spirv_type_checks(); - const u32 semantics = memory_order | location; - return spirv_asm { - result:$$T = OpAtomicAnd &dst $scope $semantics $value; - }; -} - -[[ForceInline]] -public func atomic_or( - __ref T dst, - T value, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup -) -> T { - spirv_type_checks(); - const u32 semantics = memory_order | location; - return spirv_asm { - result:$$T = OpAtomicOr &dst $scope $semantics $value; - }; -} - -[[ForceInline]] -public func atomic_xor( - __ref T dst, - T value, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup -) -> T { - spirv_type_checks(); - const u32 semantics = memory_order | location; - return spirv_asm { - result:$$T = OpAtomicXor &dst $scope $semantics $value; - }; -} - -// Floating point atomic extension ────────────────────────────────── -[[ForceInline]] -public func atomic_add( - __ref T dst, - T value, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup -) -> T { - spirv_type_checks(); - const u32 semantics = memory_order | location; - return spirv_asm { - result:$$T = OpAtomicFAddEXT &dst $scope $semantics $value; - }; -} - -[[ForceInline]] -public func atomic_max( - __ref T dst, - T value, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup -) -> T { - spirv_type_checks(); - const u32 semantics = memory_order | location; - return spirv_asm { - result:$$T = OpAtomicFMaxEXT &dst $scope $semantics $value; - }; -} - -[[ForceInline]] -public func atomic_min( - __ref T dst, - T value, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup -) -> T { - spirv_type_checks(); - const u32 semantics = memory_order | location; - return spirv_asm { - result:$$T = OpAtomicFMinEXT &dst $scope $semantics $value; - }; -} - -// Base type for atomics. Can perform very basic operations -// where it's supported without extensions. -public struct atomic { - T value; - - [[ForceInline]] - [[mutating]] - public __init(T v) { - this.store(v, std::memory_order_acq_rel); - } - - [[ForceInline]] - [[mutating]] - public func load( - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup - ) -> T { - return atomic_load(this.value, memory_order, location, scope); - } - - [[ForceInline]] - [[mutating]] - public func store( - T desired, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup - ) -> void { - atomic_store(this.value, desired, memory_order, location, scope); - } -}; - -// Integer extension ──────────────────────────────────────────────── -public extension atomic { - [[ForceInline]] - [[mutating]] - public func fetch_add( - T arg, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup - ) -> T { - return atomic_add(this.value, arg, memory_order, location, scope); - } - - [[ForceInline]] - [[mutating]] - public func fetch_sub( - T arg, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup - ) -> T { - return atomic_sub(this.value, arg, memory_order, location, scope); - } - - [[ForceInline]] - [[mutating]] - public func fetch_max( - T arg, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup - ) -> T { - return atomic_max(this.value, arg, memory_order, location, scope); - } - - [[ForceInline]] - [[mutating]] - public func fetch_min( - T arg, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup - ) -> T { - return atomic_min(this.value, arg, memory_order, location, scope); - } - - [[ForceInline]] - [[mutating]] - public func fetch_and( - T arg, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup - ) -> T { - return atomic_and(this.value, arg, memory_order, location, scope); - } - - [[ForceInline]] - [[mutating]] - public func fetch_or( - T arg, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup - ) -> T { - return atomic_or(this.value, arg, memory_order, location, scope); - } - - [[ForceInline]] - [[mutating]] - public func fetch_xor( - T arg, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup - ) -> T { - return atomic_xor(this.value, arg, memory_order, location, scope); - } -}; - -// Floating point extension ───────────────────────────────────────── -public extension atomic { - [[ForceInline]] - [[mutating]] - public func fetch_add( - T arg, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup - ) -> T { - return atomic_add(this.value, arg, memory_order, location, scope); - } - - [[ForceInline]] - [[mutating]] - public func fetch_max( - T arg, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup - ) -> T { - return atomic_max(this.value, arg, memory_order, location, scope); - } - - [[ForceInline]] - [[mutating]] - public func fetch_min( - T arg, - constexpr MemoryOrder memory_order, - constexpr MemoryLocation location = MemoryLocation::None, - constexpr MemoryScope scope = MemoryScope::Workgroup - ) -> T { - return atomic_min(this.value, arg, memory_order, location, scope); - } -}; - -// Barriers ───────────────────────────────────────────────────────── - -// Wait for all invocations in the scope restricted tangle to reach -// the current point of execution before executing further instructions. -// -// Execution is the scope defining the scope restricted tangle -// affected by this command. -// -// https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpControlBarrier -[[ForceInline]] -public func control_barrier( - constexpr MemoryOrder memory_order, - constexpr MemoryScope scope_execution = MemoryScope::Workgroup, - constexpr MemoryScope scope = MemoryScope::Workgroup, - constexpr MemoryLocation location = MemoryLocation::Workgroup -) -> void { - constexpr u32 semantics = memory_order | location; - spirv_asm { - OpControlBarrier $scope_execution $scope $semantics; - }; -} - -// Good old barrier. -[[ForceInline]] -public func memory_barrier( - constexpr MemoryOrder memory_order, - constexpr MemoryScope scope = MemoryScope::Workgroup, - constexpr MemoryLocation location = MemoryLocation::None -) -> void { - constexpr u32 semantics = memory_order | location; - spirv_asm { - OpMemoryBarrier $scope $semantics; - }; -} -} - diff --git a/Lorr/Engine/Scene/ECSModule/CoreComponents.hh b/Lorr/Engine/Scene/ECSModule/CoreComponents.hh index 9821a390..94adee66 100644 --- a/Lorr/Engine/Scene/ECSModule/CoreComponents.hh +++ b/Lorr/Engine/Scene/ECSModule/CoreComponents.hh @@ -24,11 +24,12 @@ 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) - ECS_COMPONENT_MEMBER(axis_velocity, glm::vec3, { 0.0, 0.0, 0.0 }) - ECS_COMPONENT_MEMBER(velocity_mul, f32, 1.0) + ECS_COMPONENT_MEMBER(velocity, glm::vec3, { 0.0, 0.0, 0.0 }) + ECS_COMPONENT_MEMBER(max_velocity, f32, 1.0f) + ECS_COMPONENT_MEMBER(accel_speed, f32, 1.0f) + ECS_COMPONENT_MEMBER(decel_speed, f32, 1.0f) ECS_COMPONENT_MEMBER(frustum_projection_view_mat, glm::mat4, glm::mat4(1.0)) ECS_COMPONENT_MEMBER(acceptable_lod_error, f32, 2.0f) ECS_COMPONENT_END(); @@ -36,7 +37,6 @@ ECS_COMPONENT_END(); ECS_COMPONENT_TAG(PerspectiveCamera); ECS_COMPONENT_TAG(OrthographicCamera); ECS_COMPONENT_TAG(ActiveCamera); -ECS_COMPONENT_TAG(EditorCamera); ECS_COMPONENT_BEGIN(RenderingMesh) ECS_COMPONENT_MEMBER(model_uuid, UUID, {}) @@ -66,7 +66,4 @@ ECS_COMPONENT_BEGIN(Environment) ECS_COMPONENT_MEMBER(eye_k, f32, 12.5f) ECS_COMPONENT_END(); -// Any entity with this tag won't be serialized -ECS_COMPONENT_TAG(Hidden); - // clang-format on diff --git a/Lorr/Engine/Scene/EditorCamera.cc b/Lorr/Engine/Scene/EditorCamera.cc new file mode 100644 index 00000000..80a7a2d6 --- /dev/null +++ b/Lorr/Engine/Scene/EditorCamera.cc @@ -0,0 +1,42 @@ +#include "Engine/Scene/EditorCamera.hh" + +namespace lr { +auto EditorCamera::update(this EditorCamera &self, f32 delta_time, const glm::vec3 &target_velocity) -> void { + ZoneScoped; + + auto direction = self.direction(); + auto up = glm::vec3(0.0f, 1.0f, 0.0f); + auto right = glm::normalize(glm::cross(direction, up)); + + auto cur_speed = glm::length(target_velocity); + auto acceleration_rate = cur_speed > 0.0f ? self.accel_speed : self.decel_speed; + self.velocity = glm::mix(self.velocity, target_velocity, glm::min(1.0f, acceleration_rate * delta_time)); + auto world_velocity = self.velocity.x * direction + self.velocity.y * up + self.velocity.z * right; + self.position += world_velocity * delta_time; + + auto projection_mat = glm::perspectiveRH_ZO(glm::radians(self.fov), self.aspect_ratio(), self.far_clip, self.near_clip); + projection_mat[1][1] *= -1.0f; + + auto view_mat = glm::lookAt(self.position, self.position + direction, up); + auto projection_view_mat = projection_mat * view_mat; + + self.frustum_projection_view_mat = self.projection_view_mat; + self.projection_mat = projection_mat; + self.view_mat = view_mat; + self.projection_view_mat = projection_mat * view_mat; + self.inv_view_mat = glm::inverse(view_mat); + self.inv_projection_view_mat = glm::inverse(projection_view_mat); + self.acceptable_lod_error = 2.0f; +} + +auto EditorCamera::direction(this EditorCamera &self) -> glm::vec3 { + ZoneScoped; + + auto direction = glm::vec3( + glm::cos(glm::radians(self.yaw)) * glm::cos(glm::radians(self.pitch)), + glm::sin(glm::radians(self.pitch)), + glm::sin(glm::radians(self.yaw)) * glm::cos(glm::radians(self.pitch)) + ); + return glm::normalize(direction); +} +} // namespace lr diff --git a/Lorr/Engine/Scene/EditorCamera.hh b/Lorr/Engine/Scene/EditorCamera.hh new file mode 100644 index 00000000..7f18841c --- /dev/null +++ b/Lorr/Engine/Scene/EditorCamera.hh @@ -0,0 +1,23 @@ +#pragma once + +#include "Engine/Scene/GPUScene.hh" + +namespace lr { +struct EditorCamera : GPU::Camera { + f32 yaw = 0.0f; + f32 pitch = 0.0f; + f32 fov = 65.0f; + glm::vec3 velocity = {}; + f32 max_velocity = 2.0f; + f32 accel_speed = 8.0f; + f32 decel_speed = 12.0f; + + auto update(this EditorCamera &, f32 delta_time, const glm::vec3 &target_velocity) -> void; + + auto direction(this EditorCamera &) -> glm::vec3; + auto aspect_ratio() -> f32 { + return resolution.x / resolution.y; + } +}; + +} // namespace lr diff --git a/Lorr/Engine/Scene/GPUScene.hh b/Lorr/Engine/Scene/GPUScene.hh index 2b9c98af..1c62ad10 100644 --- a/Lorr/Engine/Scene/GPUScene.hh +++ b/Lorr/Engine/Scene/GPUScene.hh @@ -55,12 +55,14 @@ struct DebugDrawer { }; enum class CullFlags : u32 { - MeshletFrustum = 1 << 0, - TriangleBackFace = 1 << 1, - MicroTriangles = 1 << 2, - Occlusion = 1 << 3, + MeshFrustum = 1 << 0, + MeshOcclusion = 1 << 1, + MeshletFrustum = 1 << 2, + MeshletOcclusion = 1 << 3, + TriangleBackFace = 1 << 4, + MicroTriangles = 1 << 5, - All = MeshletFrustum | TriangleBackFace | MicroTriangles | Occlusion, + All = ~0_u32, }; enum EnvironmentFlags : u32 { @@ -76,13 +78,13 @@ struct Environment { 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) glm::vec3 atmos_rayleigh_scatter = { 0.005802f, 0.014338f, 0.032800f }; 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_mie_asymmetry = 3.5f; + alignas(4) glm::vec3 atmos_ozone_absorption = { 0.000650f, 0.001781f, 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 }; @@ -113,8 +115,8 @@ struct Camera { alignas(4) glm::mat4 inv_projection_view_mat = {}; alignas(4) glm::mat4 frustum_projection_view_mat = {}; alignas(4) glm::vec3 position = {}; - alignas(4) f32 near_clip = {}; - alignas(4) f32 far_clip = {}; + alignas(4) f32 near_clip = 0.01f; + alignas(4) f32 far_clip = 1000.0f; alignas(4) glm::vec2 resolution = {}; alignas(4) f32 acceptable_lod_error = 0.0f; }; @@ -176,6 +178,7 @@ struct MeshInstance { alignas(4) u32 lod_index = 0; alignas(4) u32 material_index = 0; alignas(4) u32 transform_index = 0; + alignas(4) u32 meshlet_instance_visibility_offset = 0; }; struct Meshlet { @@ -207,7 +210,7 @@ struct Mesh { alignas(8) u64 vertex_positions = 0; alignas(8) u64 vertex_normals = 0; alignas(8) u64 texture_coords = 0; - alignas(4) u32 _padding = 0; + alignas(4) u32 vertex_count = 0; alignas(4) u32 lod_count = 0; alignas(8) MeshLOD lods[MAX_LODS] = {}; alignas(4) Bounds bounds = {}; diff --git a/Lorr/Engine/Scene/Scene.cc b/Lorr/Engine/Scene/Scene.cc index 2e79f8bf..7328d69f 100644 --- a/Lorr/Engine/Scene/Scene.cc +++ b/Lorr/Engine/Scene/Scene.cc @@ -4,6 +4,7 @@ #include "Engine/Core/App.hh" +#include "Engine/Math/Quat.hh" #include "Engine/Memory/Stack.hh" #include "Engine/OS/File.hh" @@ -40,29 +41,12 @@ bool json_to_quat(simdjson::ondemand::value &o, glm::quat &quat) { return true; } -auto SceneEntityDB::import_module(this SceneEntityDB &self, flecs::entity module) -> void { - ZoneScoped; - - self.imported_modules.emplace_back(module); - module.children([&](flecs::id id) { self.components.push_back(id); }); -} - -auto SceneEntityDB::is_component_known(this SceneEntityDB &self, flecs::id component_id) -> bool { - ZoneScoped; - - return std::ranges::any_of(self.components, [&](const auto &id) { return id == component_id; }); -} - -auto SceneEntityDB::get_components(this SceneEntityDB &self) -> ls::span { - return self.components; -} - auto Scene::init(this Scene &self, const std::string &name) -> bool { ZoneScoped; self.name = name; self.world.emplace(); - self.entity_db.import_module(self.world->import ()); + self.import_module(); self.world->observer() .event(flecs::OnSet) @@ -98,26 +82,6 @@ auto Scene::init(this Scene &self, const std::string &name) -> bool { } }); - self.world->observer() - .event(flecs::Monitor) // - .each([&self](flecs::iter &it, usize i, ECS::EditorCamera) { - auto entity = it.entity(i); - if (it.event() == flecs::OnAdd) { - self.editor_camera = entity; - } else if (it.event() == flecs::OnRemove) { - self.editor_camera.clear(); - } - }); - - self.world - ->system() // - .each([&](flecs::iter &it, usize, ECS::Transform &t, ECS::Camera &c) { - auto inv_orient = glm::conjugate(Math::quat_dir(t.rotation)); - t.position += glm::vec3(inv_orient * c.axis_velocity * it.delta_time()); - - c.axis_velocity = {}; - }); - self.root = self.world->entity(); self.root.add(); @@ -201,10 +165,10 @@ static auto json_to_entity(Scene &self, flecs::entity root, simdjson::ondemand:: auto component_id = world.lookup(component_name); if (!component_id) { LOG_ERROR("Entity '{}' has invalid component named '{}'!", e.name(), component_name); - return false; + continue; } - LS_EXPECT(self.get_entity_db().is_component_known(component_id)); + LS_EXPECT(self.is_component_known(component_id)); e.add(component_id); ECS::ComponentWrapper component(e, component_id); @@ -218,6 +182,7 @@ static auto json_to_entity(Scene &self, flecs::entity root, simdjson::ondemand:: std::visit( ls::match{ [](const auto &) {}, + [&](bool *v) { *v = member_json.get_bool().value_unsafe(); }, [&](f32 *v) { *v = static_cast(member_json.get_double().value_unsafe()); }, [&](i32 *v) { *v = static_cast(member_json.get_int64().value_unsafe()); }, [&](u32 *v) { *v = member_json.get_uint64().value_unsafe(); }, @@ -255,6 +220,18 @@ static auto json_to_entity(Scene &self, flecs::entity root, simdjson::ondemand:: return true; } +auto Scene::import_module(this Scene &self, flecs::entity module_entity) -> void { + ZoneScoped; + + module_entity.children([&](flecs::id id) { self.known_component_ids.push_back(id); }); +} + +auto Scene::is_component_known(this Scene &self, flecs::id component_id) -> bool { + ZoneScoped; + + return std::ranges::any_of(self.known_component_ids, [&](const auto &id) { return id == component_id; }); +} + auto Scene::import_from_file(this Scene &self, const fs::path &path) -> bool { ZoneScoped; memory::ScopedStack stack; @@ -344,6 +321,7 @@ auto entity_to_json(JsonWriter &json, flecs::entity root) -> void { std::visit( ls::match{ [](const auto &) {}, + [&](bool *v) { member_json = *v; }, [&](f32 *v) { member_json = *v; }, [&](i32 *v) { member_json = *v; }, [&](u32 *v) { member_json = *v; }, @@ -415,30 +393,15 @@ auto Scene::delete_entity(this Scene &, flecs::entity entity) -> void { entity.destruct(); } -auto Scene::create_perspective_camera( - this Scene &self, - const std::string &name, - const glm::vec3 &position, - const glm::vec3 &rotation, - f32 fov, - f32 aspect_ratio -) -> flecs::entity { +auto Scene::create_perspective_camera(this Scene &self, const std::string &name, const glm::vec3 &position, f32 yaw, f32 pitch, f32 fov) + -> flecs::entity { ZoneScoped; return self .create_entity(name) // .add() - .set({ .position = position, .rotation = glm::radians(Math::normalize_180(rotation)) }) - .set({ .fov = fov, .aspect_ratio = aspect_ratio }); -} - -auto Scene::create_editor_camera(this Scene &self) -> void { - ZoneScoped; - - self.create_perspective_camera("editor_camera", { 0.0, 2.0, 0.0 }, { 0, 0, 0 }, 65.0, 1.6) - .add() - .add() - .add() + .set({ .position = position, .rotation = { yaw, pitch, 0.0f } }) + .set({ .fov = fov }) .child_of(self.root); } @@ -484,8 +447,10 @@ auto Scene::create_model_entity(this Scene &self, UUID &importing_model_uuid) -> node_entity.set(transform_comp); if (cur_node.mesh_index.has_value()) { - node_entity.set({ .model_uuid = importing_model_uuid, - .mesh_index = static_cast(cur_node.mesh_index.value()) }); + node_entity.set({ + .model_uuid = importing_model_uuid, + .mesh_index = static_cast(cur_node.mesh_index.value()), + }); } node_entity.child_of(root); @@ -544,7 +509,7 @@ auto Scene::set_dirty(this Scene &self, flecs::entity entity) -> void { const auto *entity_transform = cur_entity.get(); const auto T = glm::translate(glm::mat4(1.0), entity_transform->position); - const auto R = glm::mat4_cast(glm::quat(entity_transform->rotation)); + const auto R = glm::mat4_cast(Math::quat_dir(entity_transform->rotation)); const auto S = glm::scale(glm::mat4(1.0), entity_transform->scale); auto local_mat = T * R * S; auto world_mat = local_mat; @@ -591,12 +556,6 @@ auto Scene::get_world(this Scene &self) -> flecs::world & { return self.world.value(); } -auto Scene::get_editor_camera(this Scene &self) -> flecs::entity { - ZoneScoped; - - return self.editor_camera; -} - auto Scene::get_name(this Scene &self) -> const std::string & { ZoneScoped; @@ -609,15 +568,11 @@ auto Scene::get_name_sv(this Scene &self) -> std::string_view { return self.name; } -auto Scene::get_entity_db(this Scene &self) -> SceneEntityDB & { - return self.entity_db; -} - auto Scene::get_cull_flags(this Scene &self) -> GPU::CullFlags & { return self.cull_flags; } -auto Scene::prepare_frame(this Scene &self, SceneRenderer &renderer) -> PreparedFrame { +auto Scene::prepare_frame(this Scene &self, SceneRenderer &renderer, ls::option override_camera) -> PreparedFrame { ZoneScoped; auto &asset_man = App::mod(); @@ -634,33 +589,39 @@ auto Scene::prepare_frame(this Scene &self, SceneRenderer &renderer) -> Prepared .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 active_camera_data = override_camera; + if (!active_camera_data.has_value()) { + camera_query.each([&active_camera_data](flecs::entity, ECS::Transform &t, ECS::Camera &c, ECS::ActiveCamera) { + auto aspect_ratio = c.resolution.x / c.resolution.y; + auto projection_mat = glm::perspectiveRH_ZO(glm::radians(c.fov), aspect_ratio, c.far_clip, c.near_clip); + projection_mat[1][1] *= -1; + + auto direction = glm::vec3( + glm::cos(glm::radians(t.rotation.x)) * glm::cos(glm::radians(t.rotation.y)), + glm::sin(glm::radians(t.rotation.y)), + glm::sin(glm::radians(t.rotation.x)) * glm::cos(glm::radians(t.rotation.y)) + ); + direction = glm::normalize(direction); + auto view_mat = glm::lookAt(t.position, t.position + direction, glm::vec3(0.0f, 1.0f, 0.0f)); + + 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{}); + GPU::Environment environment = {}; + environment_query.each([&environment](flecs::entity, ECS::Environment &environment_comp) { 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; @@ -688,6 +649,22 @@ auto Scene::prepare_frame(this Scene &self, SceneRenderer &renderer) -> Prepared environment.eye_ISO_K = environment_comp.eye_iso / environment_comp.eye_k; }); + auto regenerate_sky = false; + regenerate_sky |= self.last_environment.atmos_rayleigh_scatter != environment.atmos_rayleigh_scatter; + regenerate_sky |= self.last_environment.atmos_rayleigh_density != environment.atmos_rayleigh_density; + regenerate_sky |= self.last_environment.atmos_mie_scatter != environment.atmos_mie_scatter; + regenerate_sky |= self.last_environment.atmos_mie_density != environment.atmos_mie_density; + regenerate_sky |= self.last_environment.atmos_mie_extinction != environment.atmos_mie_extinction; + regenerate_sky |= self.last_environment.atmos_mie_asymmetry != environment.atmos_mie_asymmetry; + regenerate_sky |= self.last_environment.atmos_ozone_absorption != environment.atmos_ozone_absorption; + regenerate_sky |= self.last_environment.atmos_ozone_height != environment.atmos_ozone_height; + regenerate_sky |= self.last_environment.atmos_ozone_thickness != environment.atmos_ozone_thickness; + regenerate_sky |= self.last_environment.atmos_terrain_albedo != environment.atmos_terrain_albedo; + regenerate_sky |= self.last_environment.atmos_atmos_radius != environment.atmos_atmos_radius; + regenerate_sky |= self.last_environment.atmos_planet_radius != environment.atmos_planet_radius; + self.last_environment = environment; + + auto meshlet_instance_visibility_offset = 0_u32; auto max_meshlet_instance_count = 0_u32; auto gpu_meshes = std::vector(); auto gpu_mesh_instances = std::vector(); @@ -713,6 +690,9 @@ auto Scene::prepare_frame(this Scene &self, SceneRenderer &renderer) -> Prepared mesh_instance.lod_index = lod0_index; mesh_instance.material_index = SlotMap_decode_id(primitive.material_id).index; mesh_instance.transform_index = SlotMap_decode_id(transform_id).index; + mesh_instance.meshlet_instance_visibility_offset = meshlet_instance_visibility_offset; + + meshlet_instance_visibility_offset += lod0.meshlet_count; max_meshlet_instance_count += lod0.meshlet_count; } } @@ -732,10 +712,19 @@ auto Scene::prepare_frame(this Scene &self, SceneRenderer &renderer) -> Prepared }; 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 = asset_man.get_material(id); + auto dirty_material_indices = std::vector(); + for (const auto dirty_id : dirty_material_ids) { + const auto *material = asset_man.get_material(dirty_id); + if (!material) { + continue; + } + + auto dirty_index = SlotMap_decode_id(dirty_id).index; + dirty_material_indices.push_back(dirty_index); + if (dirty_index >= self.gpu_materials.size()) { + self.gpu_materials.resize(dirty_index + 1, {}); + } + 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); @@ -755,32 +744,35 @@ auto Scene::prepare_frame(this Scene &self, SceneRenderer &renderer) -> Prepared flags |= metallic_roughness_image_index.has_value() ? GPU::MaterialFlag::HasMetallicRoughnessImage : GPU::MaterialFlag::None; flags |= occlusion_image_index.has_value() ? GPU::MaterialFlag::HasOcclusionImage : GPU::MaterialFlag::None; - gpu_material.albedo_color = material->albedo_color; - gpu_material.emissive_color = material->emissive_color; - gpu_material.roughness_factor = material->roughness_factor; - gpu_material.metallic_factor = material->metallic_factor; - gpu_material.alpha_cutoff = material->alpha_cutoff; - gpu_material.flags = flags; - gpu_material.sampler_index = sampler_index; - gpu_material.albedo_image_index = albedo_image_index.value_or(0_u32); - gpu_material.normal_image_index = normal_image_index.value_or(0_u32); - gpu_material.emissive_image_index = emissive_image_index.value_or(0_u32); - gpu_material.metallic_roughness_image_index = metallic_roughness_image_index.value_or(0_u32); - gpu_material.occlusion_image_index = occlusion_image_index.value_or(0_u32); - - index = SlotMap_decode_id(id).index; + auto gpu_material = GPU::Material { + .albedo_color = material->albedo_color, + .emissive_color = material->emissive_color, + .roughness_factor = material->roughness_factor, + .metallic_factor = material->metallic_factor, + .alpha_cutoff = material->alpha_cutoff, + .flags = flags, + .sampler_index = sampler_index, + .albedo_image_index = albedo_image_index.value_or(0_u32), + .normal_image_index = normal_image_index.value_or(0_u32), + .emissive_image_index = emissive_image_index.value_or(0_u32), + .metallic_roughness_image_index = metallic_roughness_image_index.value_or(0_u32), + .occlusion_image_index = occlusion_image_index.value_or(0_u32), + }; + + self.gpu_materials[dirty_index] = gpu_material; } auto prepare_info = FramePrepareInfo{ .mesh_instance_count = self.mesh_instance_count, .max_meshlet_instance_count = self.max_meshlet_instance_count, + .regenerate_sky = regenerate_sky, .dirty_transform_ids = self.dirty_transforms, .gpu_transforms = self.transforms.slots_unsafe(), .dirty_material_indices = dirty_material_indices, - .gpu_materials = gpu_materials, + .gpu_materials = self.gpu_materials, .gpu_meshes = gpu_meshes, .gpu_mesh_instances = gpu_mesh_instances, - .environment = environment_data.value_or(GPU::Environment{}), + .environment = 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 74848e93..e73bb321 100644 --- a/Lorr/Engine/Scene/Scene.hh +++ b/Lorr/Engine/Scene/Scene.hh @@ -25,16 +25,6 @@ struct ankerl::unordered_dense::hash { }; namespace lr { -struct SceneEntityDB { - ankerl::unordered_dense::map component_icons = {}; - std::vector components = {}; - std::vector imported_modules = {}; - - auto import_module(this SceneEntityDB &, flecs::entity module) -> void; - auto is_component_known(this SceneEntityDB &, flecs::id component_id) -> bool; - auto get_components(this SceneEntityDB &) -> ls::span; -}; - struct AssetManager; enum class SceneID : u64 { Invalid = ~0_u64 }; struct Scene { @@ -42,33 +32,42 @@ private: std::string name = {}; flecs::entity root = {}; ls::option world = ls::nullopt; - SceneEntityDB entity_db = {}; - flecs::entity editor_camera = {}; + std::vector known_component_ids = {}; SlotMap transforms = {}; ankerl::unordered_dense::map entity_transforms_map = {}; ankerl::unordered_dense::map, std::vector> rendering_meshes_map = {}; - std::vector dirty_transforms = {}; + + std::vector gpu_materials = {}; + bool models_dirty = false; u32 mesh_instance_count = 0; u32 max_meshlet_instance_count = 0; GPU::CullFlags cull_flags = GPU::CullFlags::All; + GPU::Environment last_environment = {}; + public: auto init(this Scene &, const std::string &name) -> bool; auto destroy(this Scene &) -> void; + template + auto import_module(this Scene &self) -> void { + ZoneScoped; + + return self.import_module(self.world->import ()); + } + auto import_module(this Scene &, flecs::entity module_entity) -> void; + auto is_component_known(this Scene &, flecs::id component_id) -> bool; + auto import_from_file(this Scene &, const fs::path &path) -> bool; auto export_to_file(this Scene &, const fs::path &path) -> bool; auto create_entity(this Scene &, const std::string &name = {}) -> flecs::entity; auto delete_entity(this Scene &, flecs::entity entity) -> void; - // clang-format off - auto create_perspective_camera(this Scene &, const std::string &name, const glm::vec3 &position, const glm::vec3 &rotation, f32 fov, f32 aspect_ratio) -> flecs::entity; - // clang-format on - auto create_editor_camera(this Scene &) -> void; + auto create_perspective_camera(this Scene &, const std::string &name, const glm::vec3 &position, f32 yaw, f32 pitch, f32 fov) -> flecs::entity; // Model = collection of meshes. // This function imports every mesh inside the model asset. // The returning entity is a parent, "model" entity where each of @@ -79,7 +78,8 @@ public: auto find_entity(this Scene &, std::string_view name) -> flecs::entity; auto find_entity(this Scene &, u32 transform_index) -> flecs::entity; - auto prepare_frame(this Scene &, SceneRenderer &renderer) -> PreparedFrame; + // If we really want to render something, camera needs to be there + auto prepare_frame(this Scene &, SceneRenderer &renderer, ls::option override_camera = ls::nullopt) -> PreparedFrame; auto tick(this Scene &, f32 delta_time) -> bool; auto set_name(this Scene &, const std::string &name) -> void; @@ -87,12 +87,14 @@ public: auto get_root(this Scene &) -> flecs::entity; auto get_world(this Scene &) -> flecs::world &; - auto get_editor_camera(this Scene &) -> flecs::entity; auto get_name(this Scene &) -> const std::string &; auto get_name_sv(this Scene &) -> std::string_view; - auto get_entity_db(this Scene &) -> SceneEntityDB &; auto get_cull_flags(this Scene &) -> GPU::CullFlags &; + auto get_known_component_ids(this Scene &self) -> auto { + return ls::span(self.known_component_ids); + } + private: 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 9c4fff7a..83f2ac6d 100644 --- a/Lorr/Engine/Scene/SceneRenderer.cc +++ b/Lorr/Engine/Scene/SceneRenderer.cc @@ -5,6 +5,7 @@ #include "Engine/Core/App.hh" #include "Engine/Graphics/VulkanDevice.hh" +#include "Engine/Memory/Stack.hh" namespace lr { enum BindlessDescriptorLayout : u32 { @@ -12,12 +13,25 @@ enum BindlessDescriptorLayout : u32 { SampledImages = 1, }; +static constexpr auto sampler_min_clamp_reduction_mode = VkSamplerReductionModeCreateInfo{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO, + .pNext = nullptr, + .reductionMode = VK_SAMPLER_REDUCTION_MODE_MIN, +}; +static constexpr auto hiz_sampler_info = vuk::SamplerCreateInfo{ + .pNext = &sampler_min_clamp_reduction_mode, + .magFilter = vuk::Filter::eLinear, + .minFilter = vuk::Filter::eLinear, + .mipmapMode = vuk::SamplerMipmapMode::eNearest, + .addressModeU = vuk::SamplerAddressMode::eClampToEdge, + .addressModeV = vuk::SamplerAddressMode::eClampToEdge, +}; + auto SceneRenderer::init(this SceneRenderer &self) -> bool { ZoneScoped; 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); @@ -26,6 +40,7 @@ auto SceneRenderer::init(this SceneRenderer &self) -> bool { .definitions = { #ifdef LS_DEBUG { "ENABLE_ASSERTIONS", "1" }, + { "DEBUG_DRAW", "1" }, #endif // DEBUG { "CULLING_MESH_COUNT", "64" }, { "CULLING_MESHLET_COUNT", std::to_string(Model::MAX_MESHLET_INDICES) }, @@ -101,11 +116,11 @@ auto SceneRenderer::init(this SceneRenderer &self) -> bool { }; Pipeline::create(device, default_slang_session, generate_cull_commands_pipeline_info).value(); - auto vis_select_lods_pipeline_info = PipelineCompileInfo{ - .module_name = "passes.select_lods", + auto vis_cull_meshes_pipeline_info = PipelineCompileInfo{ + .module_name = "passes.cull_meshes", .entry_points = { "cs_main" }, }; - Pipeline::create(device, default_slang_session, vis_select_lods_pipeline_info).value(); + Pipeline::create(device, default_slang_session, vis_cull_meshes_pipeline_info).value(); auto vis_cull_meshlets_pipeline_info = PipelineCompileInfo{ .module_name = "passes.cull_meshlets", @@ -175,64 +190,23 @@ auto SceneRenderer::init(this SceneRenderer &self) -> bool { }; 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(device, default_slang_session, hiz_pipeline_info).value(); - // ── SKY LUTS ──────────────────────────────────────────────────────── - 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) - .bind_buffer(0, 1, atmos) - .dispatch_invocations_per_pixel(dst); - - return std::make_tuple(dst, 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_IA(vuk::eComputeSampled) sky_transmittance_lut, - VUK_IA(vuk::eComputeRW) sky_multiscatter_lut, - VUK_BA(vuk::eComputeRead) atmos) { - cmd_list // - .bind_compute_pipeline("passes.sky_multiscattering") - .bind_sampler(0, 0, { .magFilter = vuk::Filter::eLinear, .minFilter = vuk::Filter::eLinear }) - .bind_image(0, 1, sky_transmittance_lut) - .bind_buffer(0, 2, atmos) - .bind_image(0, 3, sky_multiscatter_lut) - .dispatch_invocations_per_pixel(sky_multiscatter_lut); - - return std::make_tuple(sky_transmittance_lut, sky_multiscatter_lut, 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)); + auto hiz_slow_pipeline_info = PipelineCompileInfo{ + .module_name = "passes.hiz_slow", + .entry_points = { "cs_main" }, + }; + Pipeline::create(device, default_slang_session, hiz_slow_pipeline_info).value(); - 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)); + auto visualize_overdraw_pipeline_info = PipelineCompileInfo{ + .module_name = "passes.visualize_overdraw", + .entry_points = { "vs_main", "fs_main" }, + }; + Pipeline::create(device, default_slang_session, visualize_overdraw_pipeline_info).value(); 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); @@ -253,16 +227,22 @@ auto SceneRenderer::prepare_frame(this SceneRenderer &self, FramePrepareInfo &in auto &transfer_man = device.transfer_man(); auto prepared_frame = PreparedFrame{}; + auto zero_fill_pass = vuk::make_pass("zero fill", [](vuk::CommandBuffer &command_buffer, VUK_BA(vuk::eTransferWrite) dst) { + command_buffer.fill_buffer(dst, 0_u32); + + return dst; + }); + if (!info.dirty_transform_ids.empty()) { 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(); + prepared_frame.transforms_buffer = self.transforms_buffer.acquire(device, "transforms", rebuild_transforms ? vuk::eNone : vuk::eMemoryRead); if (rebuild_transforms) { // If we resize buffer, we need to refill it again, so individual uploads are not required. - prepared_frame.transforms_buffer = transfer_man.upload_staging(info.gpu_transforms, self.transforms_buffer); + prepared_frame.transforms_buffer = transfer_man.upload(info.gpu_transforms, std::move(prepared_frame.transforms_buffer)); } else { // Buffer is not resized, upload individual transforms. - auto dirty_transforms_count = info.dirty_transform_ids.size(); auto dirty_transforms_size_bytes = dirty_transforms_count * sizeof(GPU::Transforms); auto upload_buffer = transfer_man.alloc_transient_buffer(vuk::MemoryUsage::eCPUtoGPU, dirty_transforms_size_bytes); @@ -295,7 +275,6 @@ auto SceneRenderer::prepare_frame(this SceneRenderer &self, FramePrepareInfo &in } ); - 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) { @@ -305,9 +284,10 @@ auto SceneRenderer::prepare_frame(this SceneRenderer &self, FramePrepareInfo &in 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(device, info.gpu_materials.size_bytes()).value(); + prepared_frame.materials_buffer = self.materials_buffer.acquire(device, "materials", rebuild_materials ? vuk::eNone : vuk::eMemoryRead); if (rebuild_materials) { - prepared_frame.materials_buffer = transfer_man.upload_staging(info.gpu_materials, self.materials_buffer); + prepared_frame.materials_buffer = transfer_man.upload(info.gpu_materials, std::move(prepared_frame.materials_buffer)); } else { // TODO: Literally repeating code, find a solution to this auto dirty_materials_count = info.dirty_material_indices.size(); @@ -340,7 +320,6 @@ auto SceneRenderer::prepare_frame(this SceneRenderer &self, FramePrepareInfo &in } ); - 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) { @@ -349,27 +328,36 @@ auto SceneRenderer::prepare_frame(this SceneRenderer &self, FramePrepareInfo &in if (!info.gpu_meshes.empty()) { 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); + prepared_frame.meshes_buffer = self.meshes_buffer.acquire(device, "meshes", vuk::eNone); + prepared_frame.meshes_buffer = transfer_man.upload(info.gpu_meshes, std::move(prepared_frame.meshes_buffer)); } else if (self.meshes_buffer) { 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(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); + prepared_frame.mesh_instances_buffer = self.mesh_instances_buffer.acquire(device, "mesh instances", vuk::eNone); + prepared_frame.mesh_instances_buffer = transfer_man.upload(info.gpu_mesh_instances, std::move(prepared_frame.mesh_instances_buffer)); + + auto mesh_instance_visibility_mask_size_bytes = (info.mesh_instance_count + 31) / 32 * sizeof(u32); + self.mesh_instance_visibility_mask_buffer = + self.mesh_instance_visibility_mask_buffer.resize(device, mesh_instance_visibility_mask_size_bytes).value(); + prepared_frame.mesh_instance_visibility_mask_buffer = + self.mesh_instance_visibility_mask_buffer.acquire(device, "mesh instance visibility mask", vuk::eNone); + prepared_frame.mesh_instance_visibility_mask_buffer = zero_fill_pass(std::move(prepared_frame.mesh_instance_visibility_mask_buffer)); + + auto meshlet_instance_visibility_mask_size_bytes = (info.max_meshlet_instance_count + 31) / 32 * sizeof(u32); + self.meshlet_instance_visibility_mask_buffer = + self.meshlet_instance_visibility_mask_buffer.resize(device, meshlet_instance_visibility_mask_size_bytes).value(); + prepared_frame.meshlet_instance_visibility_mask_buffer = + self.meshlet_instance_visibility_mask_buffer.acquire(device, "meshlet instances visibility mask", vuk::eNone); + prepared_frame.meshlet_instance_visibility_mask_buffer = zero_fill_pass(std::move(prepared_frame.meshlet_instance_visibility_mask_buffer)); } else if (self.mesh_instances_buffer) { prepared_frame.mesh_instances_buffer = self.mesh_instances_buffer.acquire(device, "mesh instances", vuk::eMemoryRead); - } - - if (info.max_meshlet_instance_count > 0) { - prepared_frame.meshlet_instances_buffer = - transfer_man.alloc_transient_buffer(vuk::MemoryUsage::eGPUonly, info.max_meshlet_instance_count * sizeof(GPU::MeshletInstance)); - prepared_frame.visible_meshlet_instances_indices_buffer = - transfer_man.alloc_transient_buffer(vuk::MemoryUsage::eGPUonly, info.max_meshlet_instance_count * sizeof(u32)); - prepared_frame.reordered_indices_buffer = transfer_man.alloc_transient_buffer( - vuk::MemoryUsage::eGPUonly, - info.max_meshlet_instance_count * Model::MAX_MESHLET_PRIMITIVES * 3 * sizeof(u32) - ); + prepared_frame.mesh_instance_visibility_mask_buffer = + self.mesh_instance_visibility_mask_buffer.acquire(device, "mesh instance visibility mask", vuk::eMemoryRead); + prepared_frame.meshlet_instance_visibility_mask_buffer = + self.meshlet_instance_visibility_mask_buffer.acquire(device, "meshlet instances visibility mask", vuk::eMemoryRead); } info.environment.transmittance_lut_size = self.sky_transmittance_lut_view.extent(); @@ -380,11 +368,682 @@ auto SceneRenderer::prepare_frame(this SceneRenderer &self, FramePrepareInfo &in prepared_frame.camera_buffer = transfer_man.scratch_buffer(info.camera); prepared_frame.mesh_instance_count = info.mesh_instance_count; + prepared_frame.max_meshlet_instance_count = info.max_meshlet_instance_count; prepared_frame.environment_flags = static_cast(info.environment.flags); + if (info.regenerate_sky || !self.sky_transmittance_lut_view) { + auto transmittance_lut_pass = vuk::make_pass( + "transmittance lut", + [](vuk::CommandBuffer &cmd_list, // + VUK_IA(vuk::eComputeRW) dst, + VUK_BA(vuk::eComputeRead) environment) { + cmd_list // + .bind_compute_pipeline("passes.sky_transmittance") + .bind_image(0, 0, dst) + .bind_buffer(0, 1, environment) + .dispatch_invocations_per_pixel(dst); + + return std::make_tuple(dst, environment); + } + ); + + prepared_frame.sky_transmittance_lut = + self.sky_transmittance_lut_view.discard(device, "sky transmittance lut", vuk::ImageUsageFlagBits::eStorage); + std::tie(prepared_frame.sky_transmittance_lut, prepared_frame.environment_buffer) = + transmittance_lut_pass(std::move(prepared_frame.sky_transmittance_lut), std::move(prepared_frame.environment_buffer)); + + auto multiscatter_lut_pass = vuk::make_pass( + "multiscatter lut", + [](vuk::CommandBuffer &cmd_list, // + VUK_IA(vuk::eComputeSampled) sky_transmittance_lut, + VUK_IA(vuk::eComputeRW) sky_multiscatter_lut, + VUK_BA(vuk::eComputeRead) environment) { + cmd_list // + .bind_compute_pipeline("passes.sky_multiscattering") + .bind_sampler(0, 0, { .magFilter = vuk::Filter::eLinear, .minFilter = vuk::Filter::eLinear }) + .bind_image(0, 1, sky_transmittance_lut) + .bind_buffer(0, 2, environment) + .bind_image(0, 3, sky_multiscatter_lut) + .dispatch_invocations_per_pixel(sky_multiscatter_lut); + + return std::make_tuple(sky_transmittance_lut, sky_multiscatter_lut, environment); + } + ); + + prepared_frame.sky_multiscatter_lut = + self.sky_multiscatter_lut_view.discard(device, "sky multiscatter lut", vuk::ImageUsageFlagBits::eStorage); + std::tie(prepared_frame.sky_transmittance_lut, prepared_frame.sky_multiscatter_lut, prepared_frame.environment_buffer) = + multiscatter_lut_pass( + std::move(prepared_frame.sky_transmittance_lut), + std::move(prepared_frame.sky_multiscatter_lut), + std::move(prepared_frame.environment_buffer) + ); + } else { + prepared_frame.sky_transmittance_lut = + self.sky_transmittance_lut_view.acquire(device, "sky transmittance lut", vuk::ImageUsageFlagBits::eSampled, vuk::Access::eComputeSampled); + prepared_frame.sky_multiscatter_lut = + self.sky_multiscatter_lut_view.acquire(device, "sky multiscatter lut", vuk::ImageUsageFlagBits::eSampled, vuk::Access::eComputeSampled); + } + return prepared_frame; } +static auto cull_meshes( + GPU::CullFlags cull_flags, + u32 mesh_instance_count, + TransferManager &transfer_man, + vuk::Value &meshes_buffer, + vuk::Value &mesh_instances_buffer, + vuk::Value &meshlet_instances_buffer, + vuk::Value &visible_meshlet_instances_count_buffer, + vuk::Value &transforms_buffer, + vuk::Value &hiz_attachment, + vuk::Value &camera_buffer, + vuk::Value &debug_drawer_buffer +) -> vuk::Value { + ZoneScoped; + + auto vis_cull_meshes_pass = vuk::make_pass( + "vis cull meshes", + [cull_flags, mesh_instance_count]( + vuk::CommandBuffer &cmd_list, + VUK_BA(vuk::eComputeRead) camera, + VUK_BA(vuk::eComputeRead) meshes, + VUK_BA(vuk::eComputeRead) transforms, + VUK_IA(vuk::eComputeSampled) hiz, + VUK_BA(vuk::eComputeRW) mesh_instances, + VUK_BA(vuk::eComputeRW) meshlet_instances, + VUK_BA(vuk::eComputeRW) visible_meshlet_instances_count, + VUK_BA(vuk::eComputeRW) debug_drawer + ) { + cmd_list // + .bind_compute_pipeline("passes.cull_meshes") + .bind_buffer(0, 0, camera) + .bind_buffer(0, 1, meshes) + .bind_buffer(0, 2, transforms) + .bind_image(0, 3, hiz) + .bind_sampler(0, 4, hiz_sampler_info) + .bind_buffer(0, 5, mesh_instances) + .bind_buffer(0, 6, meshlet_instances) + .bind_buffer(0, 7, visible_meshlet_instances_count) + .bind_buffer(0, 8, debug_drawer) + .push_constants(vuk::ShaderStageFlagBits::eCompute, 0, PushConstants(mesh_instance_count, cull_flags)) + .dispatch_invocations(mesh_instance_count); + + return std::make_tuple(camera, meshes, transforms, hiz, mesh_instances, meshlet_instances, visible_meshlet_instances_count, debug_drawer); + } + ); + + std::tie( + camera_buffer, + meshes_buffer, + transforms_buffer, + hiz_attachment, + mesh_instances_buffer, + meshlet_instances_buffer, + visible_meshlet_instances_count_buffer, + debug_drawer_buffer + ) = + vis_cull_meshes_pass( + std::move(camera_buffer), + std::move(meshes_buffer), + std::move(transforms_buffer), + std::move(hiz_attachment), + std::move(mesh_instances_buffer), + std::move(meshlet_instances_buffer), + std::move(visible_meshlet_instances_count_buffer), + std::move(debug_drawer_buffer) + ); + + auto generate_cull_commands_pass = vuk::make_pass( + "generate cull commands", + [](vuk::CommandBuffer &cmd_list, // + VUK_BA(vuk::eComputeRead) visible_meshlet_instances_count, + VUK_BA(vuk::eComputeRW) cull_meshlets_cmd) { + cmd_list // + .bind_compute_pipeline("passes.generate_cull_commands") + .bind_buffer(0, 0, visible_meshlet_instances_count) + .bind_buffer(0, 1, cull_meshlets_cmd) + .dispatch(1); + + return std::make_tuple(visible_meshlet_instances_count, cull_meshlets_cmd); + } + ); + + auto cull_meshlets_cmd_buffer = transfer_man.scratch_buffer({ .x = 0, .y = 1, .z = 1 }); + std::tie(visible_meshlet_instances_count_buffer, cull_meshlets_cmd_buffer) = + generate_cull_commands_pass(std::move(visible_meshlet_instances_count_buffer), std::move(cull_meshlets_cmd_buffer)); + + return cull_meshlets_cmd_buffer; +} + +static auto cull_meshlets( + bool late, + GPU::CullFlags cull_flags, + TransferManager &transfer_man, + vuk::Value &hiz_attachment, + vuk::Value &cull_meshlets_cmd_buffer, + vuk::Value &visible_meshlet_instances_count_buffer, + vuk::Value &visible_meshlet_instances_indices_buffer, + vuk::Value &meshlet_instance_visibility_mask_buffer, + vuk::Value &reordered_indices_buffer, + vuk::Value &meshes_buffer, + vuk::Value &mesh_instances_buffer, + vuk::Value &meshlet_instances_buffer, + vuk::Value &transforms_buffer, + vuk::Value &camera_buffer, + vuk::Value &debug_drawer_buffer +) -> vuk::Value { + ZoneScoped; + memory::ScopedStack stack; + + // ── CULL MESHLETS ─────────────────────────────────────────────────── + auto vis_cull_meshlets_pass = vuk::make_pass( + stack.format("vis cull meshlets {}", late ? "late" : "early"), + [late, cull_flags]( + vuk::CommandBuffer &cmd_list, + VUK_BA(vuk::eIndirectRead) dispatch_cmd, + VUK_BA(vuk::eComputeRead) camera, + VUK_BA(vuk::eComputeRead) meshlet_instances, + VUK_BA(vuk::eComputeRead) mesh_instances, + VUK_BA(vuk::eComputeRead) meshes, + VUK_BA(vuk::eComputeRead) transforms, + VUK_IA(vuk::eComputeSampled) hiz, + VUK_BA(vuk::eComputeRW) visible_meshlet_instances_count, + VUK_BA(vuk::eComputeRW) visible_meshlet_instances_indices, + VUK_BA(vuk::eComputeRW) meshlet_instance_visibility_mask, + VUK_BA(vuk::eComputeRW) cull_triangles_cmd, + VUK_BA(vuk::eComputeRW) debug_drawer + ) { + cmd_list // + .bind_compute_pipeline("passes.cull_meshlets") + .bind_buffer(0, 0, camera) + .bind_buffer(0, 1, meshlet_instances) + .bind_buffer(0, 2, mesh_instances) + .bind_buffer(0, 3, meshes) + .bind_buffer(0, 4, transforms) + .bind_image(0, 5, hiz) + .bind_sampler(0, 6, hiz_sampler_info) + .bind_buffer(0, 7, visible_meshlet_instances_count) + .bind_buffer(0, 8, visible_meshlet_instances_indices) + .bind_buffer(0, 9, meshlet_instance_visibility_mask) + .bind_buffer(0, 10, cull_triangles_cmd) + .bind_buffer(0, 11, debug_drawer) + .push_constants(vuk::ShaderStageFlagBits::eCompute, 0, cull_flags) + .specialize_constants(0, late) + .dispatch_indirect(dispatch_cmd); + + return std::make_tuple( + dispatch_cmd, + camera, + meshlet_instances, + mesh_instances, + meshes, + transforms, + hiz, + visible_meshlet_instances_count, + visible_meshlet_instances_indices, + meshlet_instance_visibility_mask, + cull_triangles_cmd, + debug_drawer + ); + } + ); + + auto cull_triangles_cmd_buffer = transfer_man.scratch_buffer({ .x = 0, .y = 1, .z = 1 }); + + std::tie( + cull_meshlets_cmd_buffer, + camera_buffer, + meshlet_instances_buffer, + mesh_instances_buffer, + meshes_buffer, + transforms_buffer, + hiz_attachment, + visible_meshlet_instances_count_buffer, + visible_meshlet_instances_indices_buffer, + meshlet_instance_visibility_mask_buffer, + cull_triangles_cmd_buffer, + debug_drawer_buffer + ) = + vis_cull_meshlets_pass( + std::move(cull_meshlets_cmd_buffer), + std::move(camera_buffer), + std::move(meshlet_instances_buffer), + std::move(mesh_instances_buffer), + std::move(meshes_buffer), + std::move(transforms_buffer), + std::move(hiz_attachment), + std::move(visible_meshlet_instances_count_buffer), + std::move(visible_meshlet_instances_indices_buffer), + std::move(meshlet_instance_visibility_mask_buffer), + std::move(cull_triangles_cmd_buffer), + std::move(debug_drawer_buffer) + ); + + // ── CULL TRIANGLES ────────────────────────────────────────────────── + auto vis_cull_triangles_pass = vuk::make_pass( + stack.format("vis cull triangles {}", late ? "late" : "early"), + [late, cull_flags]( + vuk::CommandBuffer &cmd_list, + VUK_BA(vuk::eIndirectRead) cull_triangles_cmd, + VUK_BA(vuk::eComputeRead) camera, + VUK_BA(vuk::eComputeRead) visible_meshlet_instances_count, + VUK_BA(vuk::eComputeRead) visible_meshlet_instances_indices, + VUK_BA(vuk::eComputeRead) meshlet_instances, + VUK_BA(vuk::eComputeRead) mesh_instances, + VUK_BA(vuk::eComputeRead) meshes, + VUK_BA(vuk::eComputeRead) transforms, + VUK_BA(vuk::eComputeRW) draw_indexed_cmd, + VUK_BA(vuk::eComputeWrite) reordered_indices + ) { + cmd_list // + .bind_compute_pipeline("passes.cull_triangles") + .bind_buffer(0, 0, camera) + .bind_buffer(0, 1, visible_meshlet_instances_count) + .bind_buffer(0, 2, visible_meshlet_instances_indices) + .bind_buffer(0, 3, meshlet_instances) + .bind_buffer(0, 4, mesh_instances) + .bind_buffer(0, 5, meshes) + .bind_buffer(0, 6, transforms) + .bind_buffer(0, 7, draw_indexed_cmd) + .bind_buffer(0, 8, reordered_indices) + .push_constants(vuk::ShaderStageFlagBits::eCompute, 0, cull_flags) + .specialize_constants(0, late) + .dispatch_indirect(cull_triangles_cmd); + + return std::make_tuple( + camera, + visible_meshlet_instances_count, + visible_meshlet_instances_indices, + meshlet_instances, + mesh_instances, + meshes, + transforms, + draw_indexed_cmd, + reordered_indices + ); + } + ); + + auto draw_command_buffer = transfer_man.scratch_buffer({ .instanceCount = 1 }); + + std::tie( + camera_buffer, + visible_meshlet_instances_count_buffer, + visible_meshlet_instances_indices_buffer, + meshlet_instances_buffer, + mesh_instances_buffer, + meshes_buffer, + transforms_buffer, + draw_command_buffer, + reordered_indices_buffer + ) = + vis_cull_triangles_pass( + std::move(cull_triangles_cmd_buffer), + std::move(camera_buffer), + std::move(visible_meshlet_instances_count_buffer), + std::move(visible_meshlet_instances_indices_buffer), + std::move(meshlet_instances_buffer), + std::move(mesh_instances_buffer), + std::move(meshes_buffer), + std::move(transforms_buffer), + std::move(draw_command_buffer), + std::move(reordered_indices_buffer) + ); + + return draw_command_buffer; +} + +static auto draw_visbuffer( + bool late, + vuk::PersistentDescriptorSet &descriptor_set, + vuk::Value &depth_attachment, + vuk::Value &visbuffer_attachment, + vuk::Value &overdraw_attachment, + vuk::Value &draw_command_buffer, + vuk::Value &reordered_indices_buffer, + vuk::Value &meshes_buffer, + vuk::Value &mesh_instances_buffer, + vuk::Value &meshlet_instances_buffer, + vuk::Value &transforms_buffer, + vuk::Value &materials_buffer, + vuk::Value &camera_buffer +) -> void { + ZoneScoped; + memory::ScopedStack stack; + + auto vis_encode_pass = vuk::make_pass( + stack.format("vis encode {}", late ? "late" : "early"), + [&descriptor_set]( + vuk::CommandBuffer &cmd_list, + VUK_BA(vuk::eIndirectRead) triangle_indirect, + VUK_BA(vuk::eIndexRead) index_buffer, + VUK_BA(vuk::eVertexRead) camera, + VUK_BA(vuk::eVertexRead) meshlet_instances, + VUK_BA(vuk::eVertexRead) mesh_instances, + VUK_BA(vuk::eVertexRead) meshes, + VUK_BA(vuk::eVertexRead) transforms, + VUK_BA(vuk::eFragmentRead) materials, + VUK_IA(vuk::eColorRW) visbuffer, + VUK_IA(vuk::eDepthStencilRW) depth, + VUK_IA(vuk::eFragmentRW) overdraw + ) { + cmd_list // + .bind_graphics_pipeline("passes.visbuffer_encode") + .set_rasterization({ .cullMode = vuk::CullModeFlagBits::eBack }) + .set_depth_stencil({ .depthTestEnable = true, .depthWriteEnable = true, .depthCompareOp = vuk::CompareOp::eGreaterOrEqual }) + .set_color_blend(visbuffer, 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_persistent(1, descriptor_set) + .bind_buffer(0, 0, camera) + .bind_buffer(0, 1, meshlet_instances) + .bind_buffer(0, 2, mesh_instances) + .bind_buffer(0, 3, meshes) + .bind_buffer(0, 4, transforms) + .bind_buffer(0, 5, materials) + .bind_image(0, 6, overdraw) + .bind_index_buffer(index_buffer, vuk::IndexType::eUint32) + .draw_indexed_indirect(1, triangle_indirect); + + return std::make_tuple( + index_buffer, + camera, + meshlet_instances, + mesh_instances, + meshes, + transforms, + materials, + visbuffer, + depth, + overdraw + ); + } + ); + + std::tie( + reordered_indices_buffer, + camera_buffer, + meshlet_instances_buffer, + mesh_instances_buffer, + meshes_buffer, + transforms_buffer, + materials_buffer, + visbuffer_attachment, + depth_attachment, + overdraw_attachment + ) = + vis_encode_pass( + std::move(draw_command_buffer), + std::move(reordered_indices_buffer), + std::move(camera_buffer), + std::move(meshlet_instances_buffer), + std::move(mesh_instances_buffer), + std::move(meshes_buffer), + std::move(transforms_buffer), + std::move(materials_buffer), + std::move(visbuffer_attachment), + std::move(depth_attachment), + std::move(overdraw_attachment) + ); +} + +static auto draw_hiz(vuk::Value &hiz_attachment, vuk::Value &depth_attachment) -> void { + ZoneScoped; + + auto hiz_generate_pass = vuk::make_pass( + "hiz generate", + [](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); + + auto dispatch_x = (extent.width + 63) >> 6; + auto dispatch_y = (extent.height + 63) >> 6; + + cmd_list // + .bind_compute_pipeline("passes.hiz") + .push_constants(vuk::ShaderStageFlagBits::eCompute, 0, PushConstants(mip_count, (dispatch_x * dispatch_y) - 1, glm::mat2(1.0f))) + .specialize_constants(0, extent.width == extent.height && (extent.width & (extent.width - 1)) == 0 ? 1u : 0u) + .specialize_constants(1, extent.width) + .specialize_constants(2, extent.height); + + *cmd_list.scratch_buffer(0, 0) = 0; + cmd_list.bind_sampler(0, 1, hiz_sampler_info); + cmd_list.bind_image(0, 2, src); + + for (u32 i = 0; i < 13; i++) { + cmd_list.bind_image(0, i + 3, dst->mip(ls::min(i, mip_count - 1_u32))); + } + + cmd_list.dispatch(dispatch_x, dispatch_y); + + return std::make_tuple(src, dst); + } + ); + + auto hiz_generate_slow_pass = vuk::make_pass( + "hiz generate slow", + [](vuk::CommandBuffer &cmd_list, // + VUK_IA(vuk::eComputeSampled) src, + VUK_IA(vuk::eComputeRW) dst) { + auto extent = dst->extent; + auto mip_count = dst->level_count; + + cmd_list // + .bind_compute_pipeline("passes.hiz_slow") + .bind_sampler(0, 0, hiz_sampler_info); + + for (auto i = 0_u32; i < mip_count; i++) { + auto mip_width = std::max(1_u32, extent.width >> i); + auto mip_height = std::max(1_u32, extent.height >> i); + + auto mip = dst->mip(i); + if (i == 0) { + cmd_list.bind_image(0, 1, src); + } else { + auto mip = dst->mip(i - 1); + cmd_list.image_barrier(mip, vuk::eComputeWrite, vuk::eComputeSampled); + cmd_list.bind_image(0, 1, mip); + } + + cmd_list.bind_image(0, 2, mip); + cmd_list.push_constants(vuk::ShaderStageFlagBits::eCompute, 0, PushConstants(mip_width, mip_height, i)); + cmd_list.dispatch_invocations(mip_width, mip_height); + } + + cmd_list.image_barrier(dst, vuk::eComputeSampled, vuk::eComputeRW); + + return std::make_tuple(src, dst); + } + ); + + std::tie(depth_attachment, hiz_attachment) = hiz_generate_slow_pass(std::move(depth_attachment), std::move(hiz_attachment)); +} + +static auto draw_sky( + SceneRenderer &self, + vuk::Value &dst_attachment, + vuk::Value &depth_attachment, + vuk::Value &sky_transmittance_lut_attachment, + vuk::Value &sky_multiscatter_lut_attachment, + vuk::Value &environment_buffer, + vuk::Value &camera_buffer +) -> void { + ZoneScoped; + + auto sky_view_lut_attachment = vuk::declare_ia( + "sky view lut", + { .image_type = vuk::ImageType::e2D, + .usage = vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eStorage, + .extent = self.sky_view_lut_extent, + .format = vuk::Format::eR16G16B16A16Sfloat, + .sample_count = vuk::Samples::e1, + .view_type = vuk::ImageViewType::e2D, + .level_count = 1, + .layer_count = 1 } + ); + + auto sky_aerial_perspective_attachment = vuk::declare_ia( + "sky aerial perspective", + { .image_type = vuk::ImageType::e3D, + .usage = vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eStorage, + .extent = self.sky_aerial_perspective_lut_extent, + .sample_count = vuk::Samples::e1, + .view_type = vuk::ImageViewType::e3D, + .level_count = 1, + .layer_count = 1 } + ); + sky_aerial_perspective_attachment.same_format_as(sky_view_lut_attachment); + + // ── 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) 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, + .addressModeU = vuk::SamplerAddressMode::eClampToEdge, + .addressModeV = vuk::SamplerAddressMode::eClampToEdge, + .addressModeW = vuk::SamplerAddressMode::eClampToEdge, + }; + + cmd_list // + .bind_compute_pipeline("passes.sky_view") + .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, 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, environment, camera, sky_view_lut); + } + ); + 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(environment_buffer), + std::move(camera_buffer), + std::move(sky_view_lut_attachment) + ); + + // ── 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) 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, + .addressModeU = vuk::SamplerAddressMode::eClampToEdge, + .addressModeV = vuk::SamplerAddressMode::eClampToEdge, + .addressModeW = vuk::SamplerAddressMode::eClampToEdge, + }; + + cmd_list // + .bind_compute_pipeline("passes.sky_aerial_perspective") + .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, 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, environment, camera, sky_aerial_perspective_lut); + } + ); + + std::tie( + sky_transmittance_lut_attachment, + sky_multiscatter_lut_attachment, + 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(environment_buffer), + std::move(camera_buffer), + std::move(sky_aerial_perspective_attachment) + ); + + // ── SKY FINAL ─────────────────────────────────────────────────────── + auto sky_final_pass = vuk::make_pass( + "sky final", + [](vuk::CommandBuffer &cmd_list, // + VUK_IA(vuk::eColorRW) 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, + .addressModeU = vuk::SamplerAddressMode::eClampToEdge, + .addressModeV = vuk::SamplerAddressMode::eClampToEdge, + .addressModeW = vuk::SamplerAddressMode::eClampToEdge, + }; + + vuk::PipelineColorBlendAttachmentState blend_info = { + .blendEnable = true, + .srcColorBlendFactor = vuk::BlendFactor::eOne, + .dstColorBlendFactor = vuk::BlendFactor::eSrcAlpha, + .colorBlendOp = vuk::BlendOp::eAdd, + .srcAlphaBlendFactor = vuk::BlendFactor::eZero, + .dstAlphaBlendFactor = vuk::BlendFactor::eOne, + .alphaBlendOp = vuk::BlendOp::eAdd, + }; + + cmd_list // + .bind_graphics_pipeline("passes.sky_final") + .set_rasterization({}) + .set_depth_stencil({}) + .set_color_blend(dst, blend_info) + .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_image(0, 1, sky_transmittance_lut) + .bind_image(0, 2, sky_aerial_perspective_lut) + .bind_image(0, 3, sky_view_lut) + .bind_image(0, 4, depth) + .bind_buffer(0, 5, environment) + .bind_buffer(0, 6, camera) + .draw(3, 1, 0, 0); + + return std::make_tuple(dst, depth, environment, camera); + } + ); + + std::tie(dst_attachment, depth_attachment, environment_buffer, camera_buffer) = sky_final_pass( + std::move(dst_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(environment_buffer), + std::move(camera_buffer) + ); +} + auto SceneRenderer::render(this SceneRenderer &self, vuk::Value &&dst_attachment, SceneRenderInfo &info, PreparedFrame &frame) -> vuk::Value { ZoneScoped; @@ -412,11 +1071,6 @@ auto SceneRenderer::render(this SceneRenderer &self, vuk::Valueextent.width + 63_u32) & ~63_u32, .height = (dst_attachment->extent.height + 63_u32) & ~63_u32, @@ -454,22 +1108,10 @@ auto SceneRenderer::render(this SceneRenderer &self, vuk::Value({ 0 }); - - std::tie( - camera_buffer, - meshes_buffer, - transforms_buffer, - mesh_instances_buffer, - meshlet_instances_buffer, - visible_meshlet_instances_count_buffer, - debug_drawer_buffer - ) = - vis_select_lods_pass( - std::move(camera_buffer), - std::move(meshes_buffer), - std::move(transforms_buffer), - std::move(mesh_instances_buffer), - std::move(meshlet_instances_buffer), - std::move(visible_meshlet_instances_count_buffer), - std::move(debug_drawer_buffer) - ); - - auto generate_cull_commands_pass = vuk::make_pass( - "generate cull commands", - [](vuk::CommandBuffer &cmd_list, // - VUK_BA(vuk::eComputeRead) visible_meshlet_instances_count, - VUK_BA(vuk::eComputeRW) cull_meshlets_cmd) { - cmd_list // - .bind_compute_pipeline("passes.generate_cull_commands") - .bind_buffer(0, 0, visible_meshlet_instances_count) - .bind_buffer(0, 1, cull_meshlets_cmd) - .dispatch(1); - - return std::make_tuple(visible_meshlet_instances_count, cull_meshlets_cmd); - } - ); - - auto cull_meshlets_cmd_buffer = transfer_man.scratch_buffer({ .x = 0, .y = 1, .z = 1 }); - std::tie(visible_meshlet_instances_count_buffer, cull_meshlets_cmd_buffer) = - generate_cull_commands_pass(std::move(visible_meshlet_instances_count_buffer), std::move(cull_meshlets_cmd_buffer)); - - // ── CULL MESHLETS ─────────────────────────────────────────────────── - auto vis_cull_meshlets_pass = vuk::make_pass( - "vis cull meshlets", - [cull_flags = info.cull_flags]( - vuk::CommandBuffer &cmd_list, - VUK_BA(vuk::eIndirectRead) dispatch_cmd, - VUK_BA(vuk::eComputeRead) camera, - VUK_BA(vuk::eComputeRead) meshlet_instances, - VUK_BA(vuk::eComputeRead) mesh_instances, - VUK_BA(vuk::eComputeRead) meshes, - VUK_BA(vuk::eComputeRead) transforms, - VUK_IA(vuk::eComputeRead) hiz, - VUK_BA(vuk::eComputeRead) visible_meshlet_instances_count, - VUK_BA(vuk::eComputeRW) cull_triangles_cmd, - VUK_BA(vuk::eComputeWrite) visible_meshlet_instances_indices, - VUK_BA(vuk::eComputeRW) debug_drawer - ) { - cmd_list // - .bind_compute_pipeline("passes.cull_meshlets") - .bind_buffer(0, 0, camera) - .bind_buffer(0, 1, meshlet_instances) - .bind_buffer(0, 2, mesh_instances) - .bind_buffer(0, 3, meshes) - .bind_buffer(0, 4, transforms) - .bind_image(0, 5, hiz) - .bind_sampler(0, 6, hiz_sampler_info) - .bind_buffer(0, 7, visible_meshlet_instances_count) - .bind_buffer(0, 8, cull_triangles_cmd) - .bind_buffer(0, 9, visible_meshlet_instances_indices) - .bind_buffer(0, 10, debug_drawer) - .push_constants(vuk::ShaderStageFlagBits::eCompute, 0, cull_flags) - .dispatch_indirect(dispatch_cmd); - - return std::make_tuple( - camera, - meshlet_instances, - mesh_instances, - meshes, - transforms, - hiz, - cull_triangles_cmd, - visible_meshlet_instances_indices, - debug_drawer - ); - } + auto visbuffer_attachment = vuk::declare_ia( + "visbuffer", + { .usage = vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eColorAttachment, + .format = vuk::Format::eR32Uint, + .sample_count = vuk::Samples::e1 } ); + visbuffer_attachment.same_shape_as(final_attachment); - auto cull_triangles_cmd_buffer = transfer_man.scratch_buffer({ .x = 0, .y = 1, .z = 1 }); - auto visible_meshlet_instances_indices_buffer = std::move(frame.visible_meshlet_instances_indices_buffer); - - std::tie( - camera_buffer, - meshlet_instances_buffer, - mesh_instances_buffer, - meshes_buffer, - transforms_buffer, - hiz_attachment, - cull_triangles_cmd_buffer, - visible_meshlet_instances_indices_buffer, - debug_drawer_buffer - ) = - vis_cull_meshlets_pass( - std::move(cull_meshlets_cmd_buffer), - std::move(camera_buffer), - std::move(meshlet_instances_buffer), - std::move(mesh_instances_buffer), - std::move(meshes_buffer), - std::move(transforms_buffer), - std::move(hiz_attachment), - std::move(visible_meshlet_instances_count_buffer), - std::move(cull_triangles_cmd_buffer), - std::move(visible_meshlet_instances_indices_buffer), - std::move(debug_drawer_buffer) - ); - - // ── CULL TRIANGLES ────────────────────────────────────────────────── - auto vis_cull_triangles_pass = vuk::make_pass( - "vis cull triangles", - [cull_flags = info.cull_flags]( - vuk::CommandBuffer &cmd_list, - VUK_BA(vuk::eIndirectRead) cull_triangles_cmd, - VUK_BA(vuk::eComputeRead) camera, - VUK_BA(vuk::eComputeRead) visible_meshlet_instances_indices, - VUK_BA(vuk::eComputeRead) meshlet_instances, - VUK_BA(vuk::eComputeRead) mesh_instances, - VUK_BA(vuk::eComputeRead) meshes, - VUK_BA(vuk::eComputeRead) transforms, - VUK_BA(vuk::eComputeRW) draw_indexed_cmd, - VUK_BA(vuk::eComputeWrite) reordered_indices - ) { - cmd_list // - .bind_compute_pipeline("passes.cull_triangles") - .bind_buffer(0, 0, camera) - .bind_buffer(0, 1, visible_meshlet_instances_indices) - .bind_buffer(0, 2, meshlet_instances) - .bind_buffer(0, 3, mesh_instances) - .bind_buffer(0, 4, meshes) - .bind_buffer(0, 5, transforms) - .bind_buffer(0, 6, draw_indexed_cmd) - .bind_buffer(0, 7, reordered_indices) - .push_constants(vuk::ShaderStageFlagBits::eCompute, 0, cull_flags) - .dispatch_indirect(cull_triangles_cmd); - - return std::make_tuple( - camera, - visible_meshlet_instances_indices, - meshlet_instances, - mesh_instances, - meshes, - transforms, - draw_indexed_cmd, - reordered_indices - ); - } + auto overdraw_attachment = vuk::declare_ia( + "overdraw", + { .usage = vuk::ImageUsageFlagBits::eSampled | vuk::ImageUsageFlagBits::eStorage, + .format = vuk::Format::eR32Uint, + .sample_count = vuk::Samples::e1 } ); + overdraw_attachment.same_shape_as(final_attachment); - auto draw_command_buffer = transfer_man.scratch_buffer({ .instanceCount = 1 }); - auto reordered_indices_buffer = std::move(frame.reordered_indices_buffer); - - std::tie( - camera_buffer, - visible_meshlet_instances_indices_buffer, - meshlet_instances_buffer, - mesh_instances_buffer, - meshes_buffer, - transforms_buffer, - draw_command_buffer, - reordered_indices_buffer - ) = - vis_cull_triangles_pass( - std::move(cull_triangles_cmd_buffer), - std::move(camera_buffer), - std::move(visible_meshlet_instances_indices_buffer), - std::move(meshlet_instances_buffer), - std::move(mesh_instances_buffer), - std::move(meshes_buffer), - std::move(transforms_buffer), - std::move(draw_command_buffer), - std::move(reordered_indices_buffer) - ); - - // ── VISBUFFER CLEAR ───────────────────────────────────────────────── auto vis_clear_pass = vuk::make_pass( "vis clear", [](vuk::CommandBuffer &cmd_list, // @@ -741,88 +1184,108 @@ auto SceneRenderer::render(this SceneRenderer &self, vuk::Value({}); + auto cull_meshlets_cmd_buffer = cull_meshes( + info.cull_flags, + frame.mesh_instance_count, + transfer_man, + meshes_buffer, + mesh_instances_buffer, + meshlet_instances_buffer, + visible_meshlet_instances_count_buffer, + transforms_buffer, + hiz_attachment, + camera_buffer, + debug_drawer_buffer ); - std::tie( + auto early_draw_visbuffer_cmd_buffer = cull_meshlets( + false, + info.cull_flags, + transfer_man, + hiz_attachment, + cull_meshlets_cmd_buffer, + visible_meshlet_instances_count_buffer, + visible_meshlet_instances_indices_buffer, + meshlet_instance_visibility_mask_buffer, + reordered_indices_buffer, + meshes_buffer, + mesh_instances_buffer, + meshlet_instances_buffer, + transforms_buffer, camera_buffer, + debug_drawer_buffer + ); + + draw_visbuffer( + false, + bindless_descriptor_set, + depth_attachment, + visbuffer_attachment, + overdraw_attachment, + early_draw_visbuffer_cmd_buffer, + reordered_indices_buffer, + meshes_buffer, + mesh_instances_buffer, meshlet_instances_buffer, + transforms_buffer, + materials_buffer, + camera_buffer + ); + + draw_hiz(hiz_attachment, depth_attachment); + + auto late_draw_visbuffer_cmd_buffer = cull_meshlets( + true, + info.cull_flags, + transfer_man, + hiz_attachment, + cull_meshlets_cmd_buffer, + visible_meshlet_instances_count_buffer, + visible_meshlet_instances_indices_buffer, + meshlet_instance_visibility_mask_buffer, + reordered_indices_buffer, + meshes_buffer, mesh_instances_buffer, + meshlet_instances_buffer, + transforms_buffer, + camera_buffer, + debug_drawer_buffer + ); + + draw_visbuffer( + true, + bindless_descriptor_set, + depth_attachment, + visbuffer_attachment, + overdraw_attachment, + late_draw_visbuffer_cmd_buffer, + reordered_indices_buffer, meshes_buffer, + mesh_instances_buffer, + meshlet_instances_buffer, transforms_buffer, materials_buffer, - visbuffer_attachment, - depth_attachment, - overdraw_attachment - ) = - vis_encode_pass( - std::move(draw_command_buffer), - std::move(reordered_indices_buffer), - std::move(camera_buffer), - std::move(meshlet_instances_buffer), - std::move(mesh_instances_buffer), - std::move(meshes_buffer), - std::move(transforms_buffer), - std::move(materials_buffer), - std::move(visbuffer_attachment), - std::move(depth_attachment), - std::move(overdraw_attachment) - ); + camera_buffer + ); // ── EDITOR MOUSE PICKING ──────────────────────────────────────────── if (info.picking_texel) { @@ -862,40 +1325,31 @@ auto SceneRenderer::render(this SceneRenderer &self, vuk::Valueextent; - auto mip_count = dst->level_count; - LS_EXPECT(mip_count < 13); - - auto dispatch_x = (extent.width + 63) >> 6; - auto dispatch_y = (extent.height + 63) >> 6; - - cmd_list // - .bind_compute_pipeline("passes.hiz") - .push_constants(vuk::ShaderStageFlagBits::eCompute, 0, PushConstants(mip_count, (dispatch_x * dispatch_y) - 1, glm::mat2(1.0f))) - .specialize_constants(0, extent.width == extent.height && (extent.width & (extent.width - 1)) == 0 ? 1u : 0u) - .specialize_constants(1, extent.width) - .specialize_constants(2, extent.height); - - *cmd_list.scratch_buffer(0, 0) = 0; - cmd_list.bind_sampler(0, 1, hiz_sampler_info); - cmd_list.bind_image(0, 2, src); - - for (u32 i = 0; i < 13; i++) { - cmd_list.bind_image(0, i + 3, dst->mip(ls::min(i, mip_count - 1_u32))); + if (self.overdraw_heatmap_scale > 0.0f) { + auto visualize_overdraw_pass = vuk::make_pass( + "visualize overdraw", + [scale = self.overdraw_heatmap_scale]( + vuk::CommandBuffer &cmd_list, // + VUK_IA(vuk::eColorRW) dst, + VUK_IA(vuk::eFragmentSampled) overdraw + ) { + cmd_list // + .bind_graphics_pipeline("passes.visualize_overdraw") + .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_image(0, 0, overdraw) + .push_constants(vuk::ShaderStageFlagBits::eFragment, 0, scale) + .draw(3, 1, 0, 0); + + return dst; } + ); - cmd_list.dispatch(dispatch_x, dispatch_y); - - return std::make_tuple(src, dst); - } - ); - - std::tie(depth_attachment, hiz_attachment) = hiz_generate_pass(std::move(depth_attachment), std::move(hiz_attachment)); + return visualize_overdraw_pass(std::move(dst_attachment), std::move(overdraw_attachment)); + } // ── VISBUFFER DECODE ──────────────────────────────────────────────── auto vis_decode_pass = vuk::make_pass( @@ -908,7 +1362,7 @@ auto SceneRenderer::render(this SceneRenderer &self, vuk::Valuedevice_address)) + .bind_buffer(0, 0, camera) .draw(3, 1, 0, 0); return std::make_tuple(dst, depth); } ); - //std::tie(result_attachment, depth_attachment) = - // editor_grid_pass(std::move(result_attachment), std::move(depth_attachment), std::move(camera_buffer)); + // std::tie(dst_attachment, depth_attachment) = editor_grid_pass(std::move(dst_attachment), std::move(depth_attachment), std::move(camera_buffer)); if (debugging) { auto debug_pass = vuk::make_pass( "debug pass", [](vuk::CommandBuffer &cmd_list, // - VUK_IA(vuk::eColorWrite) dst, + VUK_IA(vuk::eColorRW) dst, VUK_BA(vuk::eIndirectRead) indirect_buffer, VUK_BA(vuk::eVertexRead) camera, VUK_BA(vuk::eVertexRead) debug_aabb_draws, @@ -1426,6 +1720,16 @@ auto SceneRenderer::cleanup(this SceneRenderer &self) -> void { self.meshes_buffer = {}; } + if (self.mesh_instance_visibility_mask_buffer) { + device.destroy(self.mesh_instance_visibility_mask_buffer.id()); + self.mesh_instance_visibility_mask_buffer = {}; + } + + if (self.meshlet_instance_visibility_mask_buffer) { + device.destroy(self.meshlet_instance_visibility_mask_buffer.id()); + self.meshlet_instance_visibility_mask_buffer = {}; + } + if (self.materials_buffer) { device.destroy(self.materials_buffer.id()); self.materials_buffer = {}; diff --git a/Lorr/Engine/Scene/SceneRenderer.hh b/Lorr/Engine/Scene/SceneRenderer.hh index eae109e9..4adbfd75 100644 --- a/Lorr/Engine/Scene/SceneRenderer.hh +++ b/Lorr/Engine/Scene/SceneRenderer.hh @@ -8,6 +8,7 @@ namespace lr { struct FramePrepareInfo { u32 mesh_instance_count = 0; u32 max_meshlet_instance_count = 0; + bool regenerate_sky = false; ls::span dirty_transform_ids = {}; ls::span gpu_transforms = {}; @@ -24,16 +25,18 @@ struct FramePrepareInfo { struct PreparedFrame { u32 mesh_instance_count = 0; + u32 max_meshlet_instance_count = 0; GPU::EnvironmentFlags environment_flags = GPU::EnvironmentFlags::None; vuk::Value transforms_buffer = {}; vuk::Value meshes_buffer = {}; vuk::Value mesh_instances_buffer = {}; - vuk::Value meshlet_instances_buffer = {}; - vuk::Value visible_meshlet_instances_indices_buffer = {}; - vuk::Value reordered_indices_buffer = {}; + vuk::Value mesh_instance_visibility_mask_buffer = {}; + vuk::Value meshlet_instance_visibility_mask_buffer = {}; vuk::Value materials_buffer = {}; vuk::Value environment_buffer = {}; vuk::Value camera_buffer = {}; + vuk::Value sky_transmittance_lut = {}; + vuk::Value sky_multiscatter_lut = {}; }; struct SceneRenderInfo { @@ -53,11 +56,11 @@ struct SceneRenderer { Buffer mesh_instances_buffer = {}; Buffer meshes_buffer = {}; + Buffer mesh_instance_visibility_mask_buffer = {}; + Buffer meshlet_instance_visibility_mask_buffer = {}; Buffer materials_buffer = {}; - // Then what are they? - // TODO: Per scene sky settings Image sky_transmittance_lut = {}; ImageView sky_transmittance_lut_view = {}; Image sky_multiscatter_lut = {}; @@ -69,11 +72,11 @@ struct SceneRenderer { ImageView hiz_view = {}; bool debug_lines = false; + f32 overdraw_heatmap_scale = 0.0f; auto init(this SceneRenderer &) -> bool; auto destroy(this SceneRenderer &) -> void; - // Scene auto prepare_frame(this SceneRenderer &, FramePrepareInfo &info) -> PreparedFrame; auto render(this SceneRenderer &, vuk::Value &&dst_attachment, SceneRenderInfo &render_info, PreparedFrame &frame) -> vuk::Value; diff --git a/Lorr/Engine/Window/Window.cc b/Lorr/Engine/Window/Window.cc index 23da7148..673eabd3 100644 --- a/Lorr/Engine/Window/Window.cc +++ b/Lorr/Engine/Window/Window.cc @@ -93,6 +93,8 @@ auto Window::init(this Window &self) -> bool { } if (self.flags & WindowFlag::Fullscreen) { + self.width = self.display->resolution.x; + self.height = self.display->resolution.y; window_flags |= SDL_WINDOW_FULLSCREEN; } @@ -111,6 +113,7 @@ auto Window::init(this Window &self) -> bool { 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), @@ -139,6 +142,7 @@ auto Window::destroy(this Window &self) -> void { auto Window::update(this Window &self, f64) -> void { ZoneScoped; + self.mouse_moved = false; SDL_Event e = {}; while (SDL_PollEvent(&e) != 0) { switch (e.type) { @@ -146,8 +150,26 @@ auto Window::update(this Window &self, f64) -> void { auto &device = App::mod(); device.wait(); + self.width = e.window.data1; + self.height = e.window.data2; + auto surface = self.get_surface(device.get_instance()); self.swap_chain = device.create_swap_chain(surface, std::move(self.swap_chain)).value(); + + self.set_relative_mouse(SDL_GetWindowRelativeMouseMode(self.handle)); + } break; + case SDL_EVENT_KEY_DOWN: { + auto state = KeyState::Up; + state |= e.key.down ? KeyState::Down : KeyState::Up; + state |= e.key.repeat ? KeyState::Repeat : KeyState::Up; + self.key_events[e.key.scancode] = { .key = e.key.key, .mod = e.key.mod, .state = state }; + } break; + case SDL_EVENT_KEY_UP: { + self.key_events[e.key.scancode] = { .key = e.key.key, .mod = e.key.mod, .state = KeyState::Up }; + } break; + case SDL_EVENT_MOUSE_MOTION: { + self.mouse_pos = glm::vec2(e.motion.x, e.motion.y); + self.mouse_moved = true; } break; case SDL_EVENT_QUIT: { App::close(); @@ -161,6 +183,33 @@ auto Window::update(this Window &self, f64) -> void { } } +auto Window::check_key_state(this Window &self, SDL_Scancode scancode, KeyState state) -> bool { + auto it = self.key_events.find(scancode); + if (it == self.key_events.end()) { + return state == KeyState::Up; + } + + return it->second.state & state; +} + +auto Window::set_relative_mouse(this Window &self, bool enabled) -> void { + ZoneScoped; + + auto warp_rect = SDL_Rect{ .x = self.width / 2, .y = self.height / 2, .w = 1, .h = 1 }; + SDL_SetWindowMouseRect(self.handle, enabled ? &warp_rect : nullptr); + + SDL_SetWindowRelativeMouseMode(self.handle, enabled); +} + +auto Window::get_delta_mouse_pos(this Window &) -> glm::vec2 { + ZoneScoped; + + auto result = glm::vec2(); + SDL_GetRelativeMouseState(&result.x, &result.y); + + return result; +} + auto Window::set_cursor(this Window &self, WindowCursor cursor) -> void { ZoneScoped; diff --git a/Lorr/Engine/Window/Window.hh b/Lorr/Engine/Window/Window.hh index 9129490c..10f88abf 100644 --- a/Lorr/Engine/Window/Window.hh +++ b/Lorr/Engine/Window/Window.hh @@ -8,6 +8,40 @@ #include namespace lr { +enum class DialogKind : u32 { + OpenFile = 0, + SaveFile, + OpenFolder, +}; + +struct FileDialogFilter { + std::string_view name = {}; + std::string_view pattern = {}; +}; + +struct ShowDialogInfo { + DialogKind kind = DialogKind::OpenFile; + void *user_data = nullptr; + std::string_view title = {}; + fs::path spawn_path = {}; + ls::span filters = {}; + bool multi_select = false; + void (*callback)(void *user_data, const c8 *const *files, i32 filter) = nullptr; +}; + +enum class KeyState : u32 { + Up = 0, + Down = 1 << 0, + Repeat = 1 << 1, +}; +consteval void enable_bitmask(KeyState); + +struct KeyEvent { + SDL_Keycode key = {}; + SDL_Keymod mod = {}; + KeyState state = KeyState::Up; +}; + enum class WindowCursor { Arrow, TextInput, @@ -42,27 +76,6 @@ struct SystemDisplay { f32 refresh_rate = 30.0f; }; -enum class DialogKind : u32 { - OpenFile = 0, - SaveFile, - OpenFolder, -}; - -struct FileDialogFilter { - std::string_view name = {}; - std::string_view pattern = {}; -}; - -struct ShowDialogInfo { - DialogKind kind = DialogKind::OpenFile; - void *user_data = nullptr; - std::string_view title = {}; - fs::path spawn_path = {}; - ls::span filters = {}; - bool multi_select = false; - void (*callback)(void *user_data, const c8 *const *files, i32 filter) = nullptr; -}; - struct WindowInfo { std::string title = {}; SystemDisplay *display = nullptr; @@ -87,6 +100,9 @@ struct Window { glm::uvec2 cursor_position = {}; std::array cursors = {}; std::vector> event_listeners = {}; + ankerl::unordered_dense::map key_events = {}; + glm::vec2 mouse_pos = {}; + bool mouse_moved = false; static auto init_sdl() -> bool; static auto display_at(i32 monitor_id) -> ls::option; @@ -96,15 +112,19 @@ struct Window { 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; - template auto add_listener(T &listener) { event_listeners.push_back([&listener](SDL_Event &e) { listener.window_event(e); }); } + auto check_key_state(this Window &, SDL_Scancode scancode, KeyState state) -> bool; + + auto set_relative_mouse(this Window &, bool enabled) -> void; + auto get_delta_mouse_pos(this Window &) -> glm::vec2; + auto set_cursor(this Window &, WindowCursor cursor) -> void; + auto get_cursor(this Window &) -> WindowCursor; + auto show_cursor(this Window &, bool show) -> void; + auto get_size(this Window &) -> glm::ivec2; auto get_surface(this Window &, VkInstance instance) -> VkSurfaceKHR; auto get_handle(this Window &) -> void *; diff --git a/Lorr/Engine/pch.hh b/Lorr/Engine/pch.hh index e985c224..1327f8af 100755 --- a/Lorr/Engine/pch.hh +++ b/Lorr/Engine/pch.hh @@ -40,9 +40,6 @@ namespace fs = std::filesystem; #include #include -#include "Engine/Math/Quat.hh" -#include "Engine/Math/Rotation.hh" - #include "Core/Logger.hh" #include #include diff --git a/Lorr/Runtime/RuntimeModule.cc b/Lorr/Runtime/RuntimeModule.cc index 8b60dbfa..224cc780 100644 --- a/Lorr/Runtime/RuntimeModule.cc +++ b/Lorr/Runtime/RuntimeModule.cc @@ -7,6 +7,62 @@ #include "Engine/Scene/ECSModule/Core.hh" #include "Engine/Window/Window.hh" +struct Runtime { + Runtime(flecs::world &world) { + auto &window = lr::App::mod(); + world + .system() // + .each([&](flecs::iter &it, usize, lr::ECS::Transform &t, lr::ECS::Camera &c, lr::ECS::ActiveCamera) { + auto target_velocity = glm::vec3(0.0f); + if (window.check_key_state(SDL_SCANCODE_W, lr::KeyState::Down)) { + target_velocity.x = c.max_velocity; + } + + if (window.check_key_state(SDL_SCANCODE_S, lr::KeyState::Down)) { + target_velocity.x = -c.max_velocity; + } + + if (window.check_key_state(SDL_SCANCODE_D, lr::KeyState::Down)) { + target_velocity.z = c.max_velocity; + } + + if (window.check_key_state(SDL_SCANCODE_A, lr::KeyState::Down)) { + target_velocity.z = -c.max_velocity; + } + + if (window.check_key_state(SDL_SCANCODE_E, lr::KeyState::Down)) { + target_velocity.y = c.max_velocity; + } + + if (window.check_key_state(SDL_SCANCODE_Q, lr::KeyState::Down)) { + target_velocity.y = -c.max_velocity; + } + + if (window.mouse_moved) { + auto mouse_pos_delta = window.get_delta_mouse_pos(); + auto sensitivity = 0.1f; + t.rotation.x += mouse_pos_delta.x * sensitivity; + t.rotation.y -= mouse_pos_delta.y * sensitivity; + t.rotation.y = glm::clamp(t.rotation.y, -89.9f, 89.9f); + } + + auto acceleration_rate = glm::length(target_velocity) > 0.0f ? c.accel_speed : c.decel_speed; + c.velocity = glm::mix(c.velocity, target_velocity, glm::min(1.0f, acceleration_rate * it.delta_time())); + + auto direction = glm::vec3( + glm::cos(glm::radians(t.rotation.x)) * glm::cos(glm::radians(t.rotation.y)), + glm::sin(glm::radians(t.rotation.y)), + glm::sin(glm::radians(t.rotation.x)) * glm::cos(glm::radians(t.rotation.y)) + ); + direction = glm::normalize(direction); + auto up = glm::vec3(0.0f, 1.0f, 0.0f); + auto right = glm::normalize(glm::cross(direction, up)); + auto world_velocity = c.velocity.x * direction + c.velocity.y * up + c.velocity.z * right; + t.position += world_velocity * it.delta_time(); + }); + } +}; + auto RuntimeModule::init(this RuntimeModule &self) -> bool { LOG_TRACE("Actvie world: {}", self.world_path); @@ -36,7 +92,6 @@ auto RuntimeModule::update(this RuntimeModule &self, f64 delta_time) -> void { 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)); @@ -51,6 +106,7 @@ auto RuntimeModule::update(this RuntimeModule &self, f64 delta_time) -> void { if (ImGui::Begin("Runtime")) { const auto ®istry = asset_man.get_registry(); + auto loading_scene_uuid = lr::UUID(nullptr); for (const auto &[asset_uuid, asset] : registry) { if (asset.type != lr::AssetType::Scene) { continue; @@ -58,13 +114,21 @@ auto RuntimeModule::update(this RuntimeModule &self, f64 delta_time) -> void { 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); - } + loading_scene_uuid = asset_uuid; + } + } - if (asset_man.load_scene(asset_uuid)) { - self.active_scene_uuid = asset_uuid; - } + if (loading_scene_uuid) { + if (self.active_scene_uuid) { + window.set_relative_mouse(false); + asset_man.unload_scene(self.active_scene_uuid); + } + + if (asset_man.load_scene(loading_scene_uuid)) { + window.set_relative_mouse(true); + auto *scene_asset = asset_man.get_scene(loading_scene_uuid); + scene_asset->import_module(); + self.active_scene_uuid = loading_scene_uuid; } } } diff --git a/Lorr/Runtime/RuntimeModule.hh b/Lorr/Runtime/RuntimeModule.hh index 9ade34b5..e96d3998 100644 --- a/Lorr/Runtime/RuntimeModule.hh +++ b/Lorr/Runtime/RuntimeModule.hh @@ -5,6 +5,7 @@ struct RuntimeModule { static constexpr auto MODULE_NAME = "Runtime"; + bool debugging = false; fs::path world_path = {}; lr::UUID active_scene_uuid = lr::UUID(nullptr); diff --git a/Lorr/Runtime/main.cc b/Lorr/Runtime/main.cc index fe031601..32abe938 100755 --- a/Lorr/Runtime/main.cc +++ b/Lorr/Runtime/main.cc @@ -18,10 +18,17 @@ i32 main(i32 argc, c8 **argv) { lr::Window::init_sdl(); auto primary_display = lr::Window::display_at(0).value(); + auto window_info = lr::WindowInfo{ + .title = "Example Game", + .display = &primary_display, + .width = 1720, + .height = 880, + .flags = lr::WindowFlag::Fullscreen, + }; lr::AppBuilder() // - .module(3) - .module(lr::WindowInfo{ .title = "Example Game", .width = 1720, .height = 880, .flags = lr::WindowFlag::Centered }) + .module(1) + .module(window_info) .module() .module() .module() diff --git a/Lorr/ls/defer.hh b/Lorr/ls/defer.hh index bbcaba5e..31f09f13 100755 --- a/Lorr/ls/defer.hh +++ b/Lorr/ls/defer.hh @@ -9,7 +9,7 @@ template struct defer { Fn func; - defer(Fn func_): func(std::move(func_)) {} + defer(Fn func_) : func(std::move(func_)) {} ~defer() { func(); diff --git a/xmake/packages.lua b/xmake/packages.lua index 328b427a..9aba045a 100755 --- a/xmake/packages.lua +++ b/xmake/packages.lua @@ -1,3 +1,5 @@ +add_repositories("local repo", {rootdir = os.scriptdir()}) + local fmt_version = "11.2.0" local fmt_configs = { header_only = false, shared = false } add_requires("fmt " .. fmt_version, { configs = fmt_configs, system = false }) @@ -53,13 +55,13 @@ add_requires("flecs v4.0.4") add_requires("libsdl3") -add_requires("shader-slang v2025.12.1") -add_requires("vuk 2025.07.09", { configs = { +add_requires("shader-slang v2025.15") +add_requires("vuk 2025.09.01", { configs = { debug_allocations = false, disable_exceptions = false, }, debug = is_mode("debug") }) add_requires("meshoptimizer v0.24") -add_requires("ktx v4.4.0") +add_requires("ktx v4.4.0", { debug = is_plat("windows") }) add_requires("svector v1.0.3") diff --git a/xmake/repo/packages/s/shader-slang/xmake.lua b/xmake/repo/packages/s/shader-slang/xmake.lua new file mode 100644 index 00000000..c35d2391 --- /dev/null +++ b/xmake/repo/packages/s/shader-slang/xmake.lua @@ -0,0 +1,49 @@ +package("shader-slang") + set_homepage("https://github.com/shader-slang/slang") + set_description("Making it easier to work with shaders") + set_license("MIT") + + if is_host("windows") then + add_urls("https://github.com/shader-slang/slang/releases/download/v$(version)/slang-$(version)-windows-x86_64.tar.gz", + {version = function (version) return version:gsub("v", "") end}) + + add_versions("v2025.10.4", "f4199d9cb32f93410444713adfe880da2b665a9e13f2f8e23fdbff06068a9ff3") + add_versions("v2025.12.1", "02018cc923a46c434e23b166ef13c14165b0a0c4b863279731c4f6c4898fbf8e") + add_versions("v2025.15", "f37e7215e51bee4e8f5ec7b84a5d783deb6cbd0bd033c026b94f2d5a31e88d28") + elseif is_host("linux") then + add_urls("https://github.com/shader-slang/slang/releases/download/v$(version)/slang-$(version)-linux-x86_64.tar.gz", + {version = function (version) return version:gsub("v", "") end}) + + add_versions("v2025.10.4", "c2edcfdada38feb345725613c516a842700437f6fa55910b567b9058c415ce8f") + add_versions("v2025.12.1", "8f34b98391562ce6f97d899e934645e2c4466a02e66b69f69651ff1468553b27") + add_versions("v2025.15", "1eaa24f1f0483f8b8cc4b95153c815394d2f6cae08dbaf8b18d6b7975b8bbe03") + end + + on_install("windows|x64", "linux|x86_64", function (package) + os.cp("include/*.h", package:installdir("include")) + + os.trycp("lib/*slang.*", package:installdir("lib")) + os.trycp("bin/*slang.*", package:installdir("lib")) + + os.trycp("lib/*slang-glslang.*", package:installdir("lib")) + os.trycp("bin/*slang-glslang.*", package:installdir("lib")) + + os.trycp("lib/*slang-glsl-module.*", package:installdir("lib")) + os.trycp("bin/*slang-glsl-module.*", package:installdir("lib")) + + package:addenv("PATH", "bin") + end) + + on_test(function (package) + assert(package:check_cxxsnippets({ test = [[ + #include + #include + + void test() { + Slang::ComPtr global_session; + slang::createGlobalSession(global_session.writeRef()); + } + ]] }, {configs = {languages = "c++17"}})) + end) + +package_end() diff --git a/xmake/repo/packages/v/vuk/port/xmake.lua b/xmake/repo/packages/v/vuk/port/xmake.lua new file mode 100644 index 00000000..47e1c2c6 --- /dev/null +++ b/xmake/repo/packages/v/vuk/port/xmake.lua @@ -0,0 +1,81 @@ +add_rules("mode.release", "mode.debug") +set_project("vuk") + +add_requires("fmt 11.1.4", { system = false, configs = { header_only = false } }) +add_requires("vulkan-memory-allocator v3.1.0") +add_requires("concurrentqueue v1.0.4") +add_requires("plf_colony v7.41") +add_requires("robin-hood-hashing 3.11.5") +add_requires("stb 2024.06.01") +add_requires("function2 4.2.4") +add_requires("spirv-cross 1.4.309+0") +add_requires("small_vector 2024.12.23") +add_requires("vk-bootstrap v1.4.307") + +option("debug_allocations") + set_default(false) + set_showmenu(true) + add_defines("VUK_DEBUG_ALLOCATIONS=1", { force = true, public = true }) + +option("disable_exceptions") + set_default(false) + set_showmenu(true) + add_defines("VUK_DISABLE_EXCEPTIONS=1", { force = true, public = true }) + +target("vuk") + set_kind("static") + add_languages("cxx20") + add_includedirs("include/", { public = true }) + + add_files("src/*.cpp") + add_files("src/runtime/**.cpp") + + set_options("debug_allocations", "disable_exceptions") + + -- public packages + add_packages( + "fmt", + "robin-hood-hashing", + "plf_colony", + "function2", + "small_vector", + "vulkan-memory-allocator", + "vk-bootstrap", + { public = true }) + + -- private packages + add_packages( + "concurrentqueue", + "stb", + "spirv-cross", + { public = false }) + + if is_os("windows") then + add_defines( + "NOMINMAX", + "VC_EXTRALEAN", + "WIN32_LEAN_AND_MEAN", + "_CRT_SECURE_NO_WARNINGS", + "_SCL_SECURE_NO_WARNINGS", + "_SILENCE_CLANG_CONCEPTS_MESSAGE", + "_SILENCE_CXX23_ALIGNED_STORAGE_DEPRECATION_WARNING", + { public = true }) + end + + on_config(function (target) + if target:has_tool("cxx", "msvc", "cl") then + target:add("defines", "VUK_COMPILER_MSVC=1", { force = true, public = true }) + target:add("cxflags", "/permissive- /Zc:char8_t- /wd4068", { public = false }) + elseif target:has_tool("cxx", "clang_cl", "clang-cl") then + target:add("defines", "VUK_COMPILER_CLANGCL=1", { force = true, public = true }) + target:add("cxflags", "-Wno-nullability-completeness", { public = false }) + target:add("cxflags", "/permissive- /Zc:char8_t- /wd4068", { public = false }) + elseif target:has_tool("cxx", "clang", "clangxx") then + target:add("defines", "VUK_COMPILER_CLANGPP=1", { force = true, public = true }) + target:add("cxflags", "-fno-char8_t -Wno-nullability-completeness -fms-extensions", { public = false }) + elseif target:has_tool("cxx", "gcc", "gxx") then + target:add("defines", "VUK_COMPILER_GPP=1", { force = true, public = true }) + target:add("cxflags", "-fno-char8_t", { public = false }) + end + end) +target_end() diff --git a/xmake/repo/packages/v/vuk/xmake.lua b/xmake/repo/packages/v/vuk/xmake.lua new file mode 100644 index 00000000..7151378b --- /dev/null +++ b/xmake/repo/packages/v/vuk/xmake.lua @@ -0,0 +1,58 @@ +package("vuk") + set_homepage("https://github.com/martty") + set_license("MIT") + + add_urls("https://github.com/martty/vuk.git") + + add_versions("2025.02.26", "257c1629aaa4200071fb752eb24894d05ce367d4") + add_versions("2025.02.26.1", "bad2fe0a1e9c355bc2e9533e40d9783cef1b6f07") + add_versions("2025.02.26.2", "9a194d2709573e4f14a2843d127db82b2cdee9ef") + add_versions("2025.03.04", "e6143a518dff34bebbb05b43056457dea3c86b92") + add_versions("2025.03.07", "add40963bb133ec42ec6a4a5d03f8c9b880a273e") + add_versions("2025.04.08", "8a768031c3da0a9429cfdc67bb179ec1c14a1501") + add_versions("2025.04.08.1", "7831d3e7030ed51a3a6466120770718404449c6c") + add_versions("2025.04.09", "d8b6e6462c01f7ce7520671ba1fda4f3152da2c1") + add_versions("2025.04.14", "b90891644b0ac8e9f77e395fc58e3c40b4091b6c") + add_versions("2025.04.14.1", "23dbfb0b21ae426d5d2e10b2445a2f81b2257bdd") + add_versions("2025.04.15", "e4c5e487b25cb98dcc0234653ca986f8504444ee") + add_versions("2025.04.19", "75771a95ca380af9323eaffd62186ce957c793ec") + add_versions("2025.04.22", "4b9918436e48b91fc89164b54fbb3cbafb6331de") + add_versions("2025.04.28", "ceded9151342919a12a61617fc1fd5dcca99e0e4") + add_versions("2025.04.29", "024df778cfcb79d21fc63236aa5427fcb3823acf") + add_versions("2025.05.06", "73fee60d5f23bf1f14a7b1f6d8b66e19d74b956b") + add_versions("2025.06.15", "ab3bf6c51e31bdb3eb51f85845b83f939d4132de") + add_versions("2025.07.09", "8a1b873f7d0e4bb36ecd680a608a1e057655bb8c") + add_versions("2025.09.01", "c9d2aeea71fe6cd41bd10ea47c38a390546b66ab") + + add_configs("debug_allocations", { description = "Debug VMA allocations", default = false, type = "boolean" }) + add_configs("disable_exceptions", { description = "Disalbe exceptions", default = false, type = "boolean" }) + + add_deps("spirv-cross 1.4.309+0") + add_deps("function2") + + on_load(function (package) + if package:config("disable_exceptions") then + package:add("defines", "VUK_DISABLE_EXCEPTIONS=1") + end + + if package:config("debug_allocations") then + package:add("defines", "VUK_DEBUG_ALLOCATIONS=1") + end + end) + + on_install("windows|x64", "linux|x86_64", function (package) + local configs = {} + configs.debug_allocations = package:config("debug_allocations") + configs.disable_exceptions = package:config("disable_exceptions") + os.cp(path.join(os.scriptdir(), "port", "xmake.lua"), "xmake.lua") + + import("package.tools.xmake").install(package, configs) + + os.cp("include/vuk", package:installdir("include")) + end) + + on_test(function (package) + assert(package:has_cxxincludes("function2/function2.hpp")) + end) +package_end() +