From 5fb2d1e8daf7442711264c55a3a1854153bdd300 Mon Sep 17 00:00:00 2001 From: Panos Karabelas Date: Fri, 18 Oct 2024 02:29:03 +0100 Subject: [PATCH] [mesh] refactoring and initial work with meshoptimizer --- editor/Widgets/AssetBrowser.cpp | 9 +- runtime/Rendering/Mesh.cpp | 116 ++++++++++++---------- runtime/Rendering/Mesh.h | 19 ++-- runtime/Rendering/Renderer_Resources.cpp | 4 +- runtime/Resource/Import/ModelImporter.cpp | 20 +--- runtime/World/Components/Terrain.cpp | 4 +- 6 files changed, 75 insertions(+), 97 deletions(-) diff --git a/editor/Widgets/AssetBrowser.cpp b/editor/Widgets/AssetBrowser.cpp index fc42851c1..4832bafd2 100644 --- a/editor/Widgets/AssetBrowser.cpp +++ b/editor/Widgets/AssetBrowser.cpp @@ -92,9 +92,9 @@ namespace "Some models might define lights, they can be imported as well." ); - mesh_import_dialog_checkbox(MeshFlags::OptimizeVertexCache, + mesh_import_dialog_checkbox(MeshFlags::OptimizeVertexCacheAndOverdraw, "Optimize vertex cache (slower import)", - "Improve the GPU's post-transform cache hit rate, reducing the required vertex shader invocations" + "Optimize GPU's post-transform cache hit rate and reorder vertices for better cache performance, decreasing vertex shader calls and bandwidth use." ); mesh_import_dialog_checkbox(MeshFlags::OptimizeVertexFetch, @@ -102,11 +102,6 @@ namespace "Reorder vertices and changes indices to improve vertex fetch cache performance, reducing the bandwidth needed to fetch vertices" ); - mesh_import_dialog_checkbox(MeshFlags::OptimizeOverdraw, - "Optimize overdraw (slower import)", - "Minimize overdraw by reordering triangles, aiming to reduce pixel shader invocations" - ); - // Ok button if (ImGuiSp::button_centered_on_line("Ok", 0.5f)) { diff --git a/runtime/Rendering/Mesh.cpp b/runtime/Rendering/Mesh.cpp index 6f47da891..4d36c8d96 100644 --- a/runtime/Rendering/Mesh.cpp +++ b/runtime/Rendering/Mesh.cpp @@ -41,6 +41,40 @@ using namespace Spartan::Math; namespace Spartan { + namespace meshoptimizer + { + void optimize_mesh(vector& vertices, vector& indices, const uint32_t flags) + { + if (flags & static_cast(MeshFlags::OptimizeVertexCacheAndOverdraw)) + { + // optimize the order of the indices for vertex cache + meshopt_optimizeVertexCache + ( + &indices[0], // destination + &indices[0], // indices + indices.size(), // index count + vertices.size() // vertex count + ); + + // optimize triangle order to reduce overdraw - needs input from meshopt_optimizeVertexCache + meshopt_optimizeOverdraw(&indices[0], // destination + &indices[0], // indices + indices.size(), // index count + reinterpret_cast(&vertices[0]), // vertex positions + vertices.size(), // vertex count + sizeof(RHI_Vertex_PosTexNorTan), // vertex positions stride + 1.05f // threshold + ); + } + + if (flags & static_cast(MeshFlags::OptimizeVertexFetch)) + { + // optimize vertex fetch by reordering vertices based on the new index order + meshopt_optimizeVertexFetch(vertices.data(), indices.data(), indices.size(), vertices.data(), vertices.size(), sizeof(RHI_Vertex_PosTexNorTan)); + } + } + } + Mesh::Mesh() : IResource(ResourceType::Mesh) { m_flags = GetDefaultFlags(); @@ -83,10 +117,7 @@ namespace Spartan file->Read(&m_indices); file->Read(&m_vertices); - //Optimize(); - ComputeAabb(); - ComputeNormalizedScale(); - CreateGpuBuffers(); + PostProcess(); } // load foreign format else @@ -192,64 +223,16 @@ namespace Spartan return static_cast(m_indices.size()); } - void Mesh::ComputeAabb() - { - SP_ASSERT_MSG(m_vertices.size() != 0, "There are no vertices"); - - m_aabb = BoundingBox(m_vertices.data(), static_cast(m_vertices.size())); - } - uint32_t Mesh::GetDefaultFlags() { return - static_cast(MeshFlags::ImportRemoveRedundantData) | + static_cast(MeshFlags::ImportRemoveRedundantData) | static_cast(MeshFlags::ImportNormalizeScale); - //static_cast(MeshFlags::OptimizeVertexCache) | - //static_cast(MeshFlags::OptimizeOverdraw) | - //static_cast(MeshFlags::OptimizeVertexFetch); - } - - float Mesh::ComputeNormalizedScale() - { - float scale_offset = m_aabb.GetExtents().Length(); - float normalized_scale = 1.0f / scale_offset; - return normalized_scale; + //static_cast(MeshFlags::OptimizeVertexCacheAndOverdraw) | + //static_cast(MeshFlags::OptimizeVertexFetch); } - - void Mesh::Optimize() - { - SP_ASSERT(!m_indices.empty()); - SP_ASSERT(!m_vertices.empty()); - const uint32_t index_count = static_cast(m_indices.size()); - const uint32_t vertex_count = static_cast(m_vertices.size()); - const size_t vertex_size = sizeof(RHI_Vertex_PosTexNorTan); - vector indices = m_indices; - vector vertices = m_vertices; - - // vertex cache optimization - if (m_flags & static_cast(MeshFlags::OptimizeVertexCache)) - { - meshopt_optimizeVertexCache(&indices[0], &indices[0], index_count, vertex_count); - } - - // overdraw optimization - if (m_flags & static_cast(MeshFlags::OptimizeOverdraw)) - { - meshopt_optimizeOverdraw(&indices[0], &indices[0], index_count, &vertices[0].pos[0], vertex_count, vertex_size, 1.05f); - } - - // vertex fetch optimization - if (m_flags & static_cast(MeshFlags::OptimizeVertexFetch)) - { - meshopt_optimizeVertexFetch(&vertices[0], &indices[0], index_count, &vertices[0], vertex_count, vertex_size); - } - - // store the updated data back to member variables - m_indices = move(indices); - m_vertices = move(vertices); - } void Mesh::CreateGpuBuffers() { m_vertex_buffer = make_shared(RHI_Buffer_Type::Vertex, @@ -269,6 +252,29 @@ namespace Spartan ); } + void Mesh::PostProcess() + { + // work on the vertices an indices + meshoptimizer::optimize_mesh(m_vertices, m_indices, m_flags); + + // compute an axis-aligned bounding box + m_aabb = BoundingBox(m_vertices.data(), static_cast(m_vertices.size())); + + // normalize scale + if (m_flags & static_cast(MeshFlags::ImportNormalizeScale)) + { + if (shared_ptr root_entity = m_root_entity.lock()) + { + float scale_offset = m_aabb.GetExtents().Length(); + float normalized_scale = 1.0f / scale_offset; + + root_entity->SetScale(normalized_scale); + } + } + + CreateGpuBuffers(); + } + void Mesh::SetMaterial(shared_ptr& material, Entity* entity) const { SP_ASSERT(material != nullptr); diff --git a/runtime/Rendering/Mesh.h b/runtime/Rendering/Mesh.h index f278953cd..86fed4c65 100644 --- a/runtime/Rendering/Mesh.h +++ b/runtime/Rendering/Mesh.h @@ -33,13 +33,12 @@ namespace Spartan { enum class MeshFlags : uint32_t { - ImportRemoveRedundantData = 1 << 0, - ImportLights = 1 << 1, - ImportCombineMeshes = 1 << 2, - ImportNormalizeScale = 1 << 3, - OptimizeVertexCache = 1 << 4, - OptimizeVertexFetch = 1 << 5, - OptimizeOverdraw = 1 << 6, + ImportRemoveRedundantData = 1 << 0, + ImportLights = 1 << 1, + ImportCombineMeshes = 1 << 2, + ImportNormalizeScale = 1 << 3, + OptimizeVertexCacheAndOverdraw = 1 << 4, // Optimize GPU's post-transform cache hit rate and reorder vertices for better cache performance, decreasing vertex shader calls and bandwidth use. + OptimizeVertexFetch = 1 << 5, }; enum class MeshType @@ -105,11 +104,11 @@ namespace Spartan MeshType GetType() const { return m_type; } void SetType(const MeshType type) { m_type = type; } - // misc + // flags uint32_t GetFlags() const { return m_flags; } static uint32_t GetDefaultFlags(); - float ComputeNormalizedScale(); - void Optimize(); + + void PostProcess(); void SetMaterial(std::shared_ptr& material, Entity* entity) const; void AddTexture(std::shared_ptr& material, MaterialTexture texture_type, const std::string& file_path, bool is_gltf); diff --git a/runtime/Rendering/Renderer_Resources.cpp b/runtime/Rendering/Renderer_Resources.cpp index a64de60bb..520acc2a2 100644 --- a/runtime/Rendering/Renderer_Resources.cpp +++ b/runtime/Rendering/Renderer_Resources.cpp @@ -538,9 +538,7 @@ namespace Spartan mesh->AddIndices(indices); mesh->AddVertices(vertices); mesh->SetType(type); - mesh->ComputeAabb(); - mesh->ComputeNormalizedScale(); - mesh->CreateGpuBuffers(); + mesh->PostProcess(); standard_meshes[static_cast(type)] = mesh; }; diff --git a/runtime/Resource/Import/ModelImporter.cpp b/runtime/Resource/Import/ModelImporter.cpp index 76804f88a..aa24ce6dc 100644 --- a/runtime/Resource/Import/ModelImporter.cpp +++ b/runtime/Resource/Import/ModelImporter.cpp @@ -487,25 +487,7 @@ namespace Spartan this_thread::sleep_for(std::chrono::milliseconds(16)); } - // optimize - if ((mesh->GetFlags() & static_cast(MeshFlags::OptimizeVertexCache)) || - (mesh->GetFlags() & static_cast(MeshFlags::OptimizeVertexFetch)) || - (mesh->GetFlags() & static_cast(MeshFlags::OptimizeOverdraw))) - { - mesh->Optimize(); - } - - // aabb - mesh->ComputeAabb(); - - // normalize scale - if (mesh->GetFlags() & static_cast(MeshFlags::ImportNormalizeScale)) - { - float normalized_scale = mesh->ComputeNormalizedScale(); - mesh->GetRootEntity().lock()->SetScale(normalized_scale); - } - - mesh->CreateGpuBuffers(); + mesh->PostProcess(); } // make the root entity active since it's now thread-safe diff --git a/runtime/World/Components/Terrain.cpp b/runtime/World/Components/Terrain.cpp index e2eb9b1d5..c5a7dd6b0 100644 --- a/runtime/World/Components/Terrain.cpp +++ b/runtime/World/Components/Terrain.cpp @@ -686,9 +686,7 @@ namespace Spartan mesh->Clear(); mesh->AddIndices(m_tile_indices[tile_index]); mesh->AddVertices(m_tile_vertices[tile_index]); - mesh->CreateGpuBuffers(); - mesh->ComputeNormalizedScale(); - mesh->ComputeAabb(); + mesh->PostProcess(); // create a child entity, add a renderable, and this mesh tile to it {