优化3D应用网格几何序列化性能的技术咨询
问题:优化3D应用序列化耗时,Assimp+OBJ性能瓶颈
我需要提升3D应用的序列化速度,测试场景下,Blender序列化耗时1秒,3DS Max耗时2秒,但我的应用耗时高达15秒。目前我采用OBJ文件格式配合Assimp进行网格序列化,发现Assimp的序列化环节耗时过高,想请教两个问题:
- 是否应该改用自定义序列化格式?
- 有哪些可以参考的优化技巧?
附上当前的序列化代码:
aiScene* scene = new aiScene(); // Allocate materials memory scene->mNumMaterials = 1; scene->mMaterials = new aiMaterial*[scene->mNumMaterials]; // Allocate mesh memory scene->mNumMeshes = (unsigned)m_flatMeshContainer.size(); scene->mMeshes = new aiMesh*[m_flatMeshContainer.size()]; scene->mRootNode = new aiNode(); scene->mRootNode->mNumMeshes = 0; // Create materials for (Uint32 i = 0u; i < scene->mNumMaterials; ++i) { scene->mMaterials[i] = new aiMaterial(); } // Create meshes std::mutex mtx; tbb::parallel_for(size_t(0), m_meshGroupIdentifiers.size(), [&](size_t idx) { const auto& groupId = m_meshGroupIdentifiers[idx]; const auto group = getMeshGroupPtr_FromEntity(groupId); // Create meshes const auto nbMeshes = (unsigned)group->m_meshes.size(); aiNode* node = new aiNode(); node->mMeshes = new unsigned int[nbMeshes]; node->mNumMeshes = nbMeshes; for (Uint32 i = 0u; i < nbMeshes; ++i) { // Init mesh const Mesh* nativeMesh = group->m_meshes[i]; const auto flatMeshIdx = nativeMesh->getFlatId(); scene->mMeshes[flatMeshIdx] = new aiMesh(); node->mMeshes[i] = flatMeshIdx; aiMesh* mesh = scene->mMeshes[flatMeshIdx]; mesh->mName = group->getName(); mesh->mMaterialIndex = 0; // Build vertices const auto& vertices = nativeMesh->getRealVertices(); const auto nbVertex = (unsigned)vertices.size(); mesh->mVertices = new aiVector3D[nbVertex]; mesh->mNormals = new aiVector3D[nbVertex]; mesh->mNumVertices = nbVertex; mesh->mTextureCoords[0] = new aiVector3D[nbVertex]; mesh->mNumUVComponents[0] = nbVertex; for (Uint32 j = 0u; j < nbVertex; ++j) { const auto& vtx = vertices[j]; mesh->mVertices[j] = aiVector3D(vtx.position.x, vtx.position.y, vtx.position.z); mesh->mNormals[j] = aiVector3D(vtx.normal.x, vtx.normal.y, vtx.normal.z); mesh->mTextureCoords[0][j] = aiVector3D(vtx.texCoord.x, vtx.texCoord.y, 0); } // Build faces const auto& indices = nativeMesh->getRealIndices(); mesh->mNumFaces = (unsigned)indices.size() / PRIMITIVE_NB_VTX; mesh->mFaces = new aiFace[mesh->mNumFaces]; for (Uint32 j = 0u; j < indices.size(); j += PRIMITIVE_NB_VTX) { aiFace &face = mesh->mFaces[j / PRIMITIVE_NB_VTX]; face.mIndices = new unsigned int[PRIMITIVE_NB_VTX]; face.mNumIndices = PRIMITIVE_NB_VTX; for (Uint32 k = 0; k < PRIMITIVE_NB_VTX; ++k) { face.mIndices[k] = indices[k + j]; } } } std::lock_guard<std::mutex> lock(mtx); scene->mRootNode->addChildren(1, &node); }); const auto objPath = m_serializationFullPath.string(); Assimp::Exporter exporter; // HERE : SLOW -------------------------------------------------------------------------------------------- exporter.Export(scene, "obj", objPath); // --------------------------------------------------------------------------------------------------------- delete scene;
回答
是否应该使用自定义格式?
如果你的应用仅需内部场景数据传递,无需兼容第三方工具,自定义二进制格式是最优选择——OBJ作为文本格式,本身的IO和解析开销远高于二进制格式,且Assimp的OBJ导出器为了兼容性做了大量冗余处理,速度自然受限。
如果必须兼容第三方工具,优先选择二进制3D格式(如glTF二进制版、FBX二进制版),这类格式的序列化效率远高于OBJ。
优化技巧参考
1. 针对Assimp的优化
- 切换导出格式:放弃OBJ,改用
glb(glTF二进制)或fbx(二进制)导出,Assimp对这类格式的导出效率远高于文本格式,且二进制IO本身速度更快。 - 关闭冗余处理:创建
Exporter时,通过属性设置关闭不必要的校验、转换逻辑,示例:exporter.SetPropertyBool(AI_CONFIG_EXPORT_FBX_EMIT_TRIANGLES, true); // 直接输出三角面,避免格式转换 exporter.SetPropertyBool(AI_CONFIG_EXPORT_REMOVE_REDUNDANT_MATERIALS, true); // 移除冗余材质 - 复用资源:若频繁执行序列化,避免每次创建新的
aiScene和Exporter,复用已分配的内存结构减少分配/释放开销。
2. 代码层面的优化
- 减少内存分配:当前代码大量使用
new创建数组(如aiVector3D、aiFace),可改用内存池或std::vector管理内存,避免频繁堆操作。 - 优化并行逻辑:创建
aiMesh时,scene->mMeshes的访问因flatMeshIdx唯一无需加锁,仅保留添加节点时的锁;或批量添加节点,减少锁竞争次数。 - 避免数据拷贝:当前代码将原生Mesh数据逐个拷贝到
aiMesh,开销极大。若能让aiMesh直接指向原生数据内存(需保证数据生命周期),可大幅降低拷贝耗时。
3. 其他方向
- 异步IO:将文件写入操作放到异步线程,避免阻塞主线程(仅隐藏耗时,不减少总耗时)。
- 轻量压缩:若使用自定义二进制格式,加入LZ4等轻量压缩算法,减少IO量,压缩/解压开销远低于文本格式处理开销。
- 合并网格:将场景中大量小网格合并为少量大网格,减少Assimp处理的节点和Mesh数量,降低序列化开销。
内容的提问来源于stack exchange,提问作者TheChamp
相关产品推荐
相关产品推荐

