From 3b518dc17dee84cb2ad3f51c6a086cc173795040 Mon Sep 17 00:00:00 2001 From: Hugues Delorme Date: Fri, 25 Mar 2016 12:41:18 +0100 Subject: [PATCH] benchmarks: add benchmark for Lib3MF --- CMakeLists.txt | 6 - benchmarks/CMakeLists.txt | 16 ++ benchmarks/benchmark_lib3mf/CMakeLists.txt | 37 +++ benchmarks/benchmark_lib3mf/main.cpp | 286 +++++++++++++++++++++ 4 files changed, 339 insertions(+), 6 deletions(-) create mode 100644 benchmarks/benchmark_lib3mf/CMakeLists.txt create mode 100644 benchmarks/benchmark_lib3mf/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ca08ae..f4d7777 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,12 +55,6 @@ endif() option(GMIO_BUILD_STRICT_C90 "Build gmio library(and tests) with strict conformance to C90 standard" OFF) option(GMIO_BUILD_SHARED_LIBS "Build gmio as a shared library (DLL)" OFF) option(GMIO_BUILD_BENCHMARKS "Build performance benchmarks for the gmio library" OFF) -cmake_dependent_option( - GMIO_BUILD_BENCHMARK_ASSIMP "Build benchmark for Assimp" ON - "GMIO_BUILD_BENCHMARKS" OFF) -cmake_dependent_option( - GMIO_BUILD_BENCHMARK_OPENCASCADE "Build benchmark for OpenCascade" ON - "GMIO_BUILD_BENCHMARKS" OFF) option(GMIO_BUILD_EXAMPLES "Build gmio examples" OFF) option(GMIO_BUILD_TESTS_FAKE_SUPPORT "Build tests/fake_support target" OFF) if(CMAKE_C_COMPILER_IS_GCC_COMPATIBLE) diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 2bbb2c1..4e1863c 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -13,6 +13,18 @@ ## "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html". ############################################################################# +cmake_dependent_option( + GMIO_BUILD_BENCHMARK_ASSIMP "Build benchmark for Assimp" ON + "GMIO_BUILD_BENCHMARKS" OFF) +cmake_dependent_option( + GMIO_BUILD_BENCHMARK_OPENCASCADE "Build benchmark for OpenCascade" ON + "GMIO_BUILD_BENCHMARKS" OFF) +if(WIN32) + cmake_dependent_option( + GMIO_BUILD_BENCHMARK_LIB3MF "Build benchmark for Lib3MF" ON + "GMIO_BUILD_BENCHMARKS" OFF) +endif() + file(GLOB COMMONS_FILES commons/*) set(COMMONS_FILES ${COMMONS_FILES}) @@ -34,4 +46,8 @@ if(GMIO_BUILD_BENCHMARK_OPENCASCADE) add_subdirectory(benchmark_opencascade) endif() +if(GMIO_BUILD_BENCHMARK_LIB3MF) + add_subdirectory(benchmark_lib3mf) +endif() + add_subdirectory(benchmark_other) diff --git a/benchmarks/benchmark_lib3mf/CMakeLists.txt b/benchmarks/benchmark_lib3mf/CMakeLists.txt new file mode 100644 index 0000000..f9a1a72 --- /dev/null +++ b/benchmarks/benchmark_lib3mf/CMakeLists.txt @@ -0,0 +1,37 @@ +############################################################################# +## gmio +## Copyright Fougue (2 Mar. 2015) +## contact@fougue.pro +## +## This software is a reusable library whose purpose is to provide complete +## I/O support for various CAD file formats (eg. STL) +## +## This software is governed by the CeCILL-B license under French law and +## abiding by the rules of distribution of free software. You can use, +## modify and/ or redistribute the software under the terms of the CeCILL-B +## license as circulated by CEA, CNRS and INRIA at the following URL +## "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html". +############################################################################# + +set(ROOTDIR_LIB3MF ${CMAKE_SOURCE_DIR} CACHE PATH + "Directory where the Lib3MF library resides") + +if(CMAKE_C_COMPILER_IS_GCC_COMPATIBLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +endif() + +include_directories(${ROOTDIR_LIB3MF}/Include) +add_executable(benchmark_lib3mf main.cpp ${COMMONS_FILES}) + +if(GMIO_TARGET_ARCH_BIT_SIZE EQUAL 64) + set(LIB3MF_TARGET_ARCH "x64") +elseif(GMIO_TARGET_ARCH_BIT_SIZE EQUAL 32) + set(LIB3MF_TARGET_ARCH "x86") +else() + message(FATAL_ERROR "Target architecture not supported") +endif() + +find_library( + LIB_LIB3MF Lib3MF ${ROOTDIR_LIB3MF}/Project/Lib3MF/${LIB3MF_TARGET_ARCH}/${CMAKE_BUILD_TYPE} + DOC "Path to the lib3mf import library") +target_link_libraries(benchmark_lib3mf ${LIB_LIB3MF}) diff --git a/benchmarks/benchmark_lib3mf/main.cpp b/benchmarks/benchmark_lib3mf/main.cpp new file mode 100644 index 0000000..4d611c8 --- /dev/null +++ b/benchmarks/benchmark_lib3mf/main.cpp @@ -0,0 +1,286 @@ +/**************************************************************************** +** gmio benchmarks +** Copyright Fougue (2 Mar. 2015) +** contact@fougue.pro +** +** This software provides performance benchmarks for the gmio library +** (https://github.com/fougue/gmio) +** +** This software is governed by the CeCILL-B license under French law and +** abiding by the rules of distribution of free software. You can use, +** modify and/ or redistribute the software under the terms of the CeCILL-B +** license as circulated by CEA, CNRS and INRIA at the following URL +** "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html". +****************************************************************************/ + +#include "../commons/benchmark_tools.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace BmkLib3MF { + +NMR::CMesh globalMesh; + +std::wstring multibyte_to_wstring(const char* str, int codepage = 0) +{ + const int str_len = static_cast(std::strlen(str)); + const int wlen = MultiByteToWideChar(codepage, 0, str, str_len, nullptr, 0); + std::wstring wstr; + wstr.resize(wlen + 1); + const int wstr_len = static_cast(wstr.size()); + MultiByteToWideChar(codepage, 0, str, str_len, &wstr[0], wstr_len); + return wstr; +} + +static void import(const void* filepath) +{ + const char* cstr_filepath = static_cast(filepath); + const std::wstring wstr_filepath = multibyte_to_wstring(cstr_filepath); + auto istream = std::make_shared(wstr_filepath.c_str()); + NMR::CMeshImporter_STL importer(istream); + importer.setImportColors(false); + importer.setIgnoreInvalidFaces(true); + try { + importer.loadMesh(&globalMesh, nullptr); + if (GetLastError() != ERROR_SUCCESS) + std::cerr << "NMR::CMeshImporter_STL error" << std::endl; + } + catch(...) { + std::cerr << "NMR::CMeshImporter_STL::loadMesh() exception" << std::endl; + } +} + +static void export_binary(const void* filepath) +{ + const char* cstr_filepath = static_cast(filepath); + const std::wstring wstr_filepath = multibyte_to_wstring(cstr_filepath); + auto ostream = std::make_shared(wstr_filepath.c_str()); + NMR::CMeshExporter_STL exporter(ostream); + exporter.exportMesh(&globalMesh, nullptr); + if (GetLastError() != ERROR_SUCCESS) + std::cerr << "NMR::CMeshExporter_STL error" << std::endl; +} + +} // namespace BmkLib3MF + +namespace BmkGmio { + +struct NMR_CMeshHelper +{ + NMR::CMesh mesh; + const NMR::NMATRIX3* matrix; + NMR::CVectorTree vectorTree; + bool ignoreInvalidFaces; +}; + +NMR_CMeshHelper globalMeshHelper; + +static void get_triangle( + const void* cookie, uint32_t tri_id, gmio_stl_triangle* triangle) +{ + // Note: code from lib3mf/Source/Common/MeshImport/NMR_MeshImporter_STL.cpp + // Commit #a466df4 + + auto constMeshHelper = static_cast(cookie); + auto meshHelper = const_cast(constMeshHelper); + NMR::CMesh* pMesh = &meshHelper->mesh; + const NMR::NMATRIX3* pmMatrix = meshHelper->matrix; + NMR::MESHFACE* face = pMesh->getFace(tri_id); + NMR::MESHFORMAT_STL_FACET facet; + + for (int j = 0; j < 3; j++) { + NMR::MESHNODE* node = pMesh->getNode(face->m_nodeindices[j]); + if (pmMatrix) + facet.m_vertieces[j] = NMR::fnMATRIX3_apply(*pmMatrix, node->m_position); + else + facet.m_vertieces[j] = node->m_position; + } + + // Calculate Triangle Normals + const NMR::NVEC3 n = + NMR::fnVEC3_calcTriangleNormal( + facet.m_vertieces[0], facet.m_vertieces[1], facet.m_vertieces[2]); + std::memcpy(&triangle->n, &n, sizeof(gmio_vec3f)); + std::memcpy(&triangle->v1, &facet.m_vertieces[0], sizeof(gmio_vec3f)); + std::memcpy(&triangle->v2, &facet.m_vertieces[1], sizeof(gmio_vec3f)); + std::memcpy(&triangle->v3, &facet.m_vertieces[2], sizeof(gmio_vec3f)); + triangle->attribute_byte_count = 0; +} + +static void add_triangle( + void* cookie, uint32_t /*tri_id*/, const gmio_stl_triangle* triangle) +{ + // Note: code from lib3mf/Source/Common/MeshExport/NMR_MeshExporter_STL.cpp + // Commit #a466df4 + + auto meshHelper = static_cast(cookie); + NMR::CMesh* pMesh = &meshHelper->mesh; + const NMR::NMATRIX3* pmMatrix = meshHelper->matrix; + NMR::CVectorTree& VectorTree = meshHelper->vectorTree; + NMR::MESHNODE* pNodes[3]; + + // Check, if Coordinates are in Valid Space + bool bIsValid = true; + { + const gmio_vec3f* vertex_ptr = &triangle->v1; + for (int i = 0; i < 3; i++) { + const float* coord_ptr = &vertex_ptr[i].x; + for (int j = 0; j < 3; j++) + bIsValid &= (fabs(coord_ptr[j]) < NMR_MESH_MAXCOORDINATE); + } + } + + // Identify Nodes via Tree + if (bIsValid) { + const gmio_vec3f* vertex_ptr = &triangle->v1; + for (int j = 0; j < 3; j++) { + NMR::NVEC3 vPosition; + std::memcpy(&vPosition, &vertex_ptr[j], sizeof(NMR::NVEC3)); + if (pmMatrix) + vPosition = NMR::fnMATRIX3_apply(*pmMatrix, vPosition); + + uint32_t nNodeIdx; + if (VectorTree.findVector3(vPosition, nNodeIdx)) { + pNodes[j] = pMesh->getNode(nNodeIdx); + } + else { + pNodes[j] = pMesh->addNode(vPosition); + VectorTree.addVector3( + pNodes[j]->m_position, + (uint32_t)pNodes[j]->m_index); + } + } + + // check, if Nodes are separate + bIsValid = (pNodes[0] != pNodes[1]) + && (pNodes[0] != pNodes[2]) + && (pNodes[1] != pNodes[2]); + } + + // Throw "Invalid Exception" + if ((!bIsValid) && !meshHelper->ignoreInvalidFaces) + throw NMR::CNMRException(NMR_ERROR_INVALIDCOORDINATES); + + if (bIsValid) + pMesh->addFace(pNodes[0], pNodes[1], pNodes[2]); +} + +static void stl_read(const void* filepath) +{ + const char* str_filepath = static_cast(filepath); + gmio_stl_mesh_creator mesh_creator = {}; + mesh_creator.cookie = &globalMeshHelper; + mesh_creator.func_add_triangle = add_triangle; + const int error = gmio_stl_read_file(str_filepath, &mesh_creator, NULL); + if (error != GMIO_ERROR_OK) + std::cout << "gmio error: 0x" << std::hex << error << std::endl; +} + +static void stl_write_generic(const char* filepath, gmio_stl_format format) +{ + gmio_stl_mesh mesh = {}; + mesh.cookie = &globalMeshHelper; + mesh.triangle_count = globalMeshHelper.mesh.getFaceCount(); + mesh.func_get_triangle = get_triangle; + + gmio_stl_write_options opts = {}; + opts.stla_solid_name = filepath; + opts.stla_float32_prec = 7; + opts.stlb_header = gmio_stlb_header_str(filepath); + const int error = gmio_stl_write_file(format, filepath, &mesh, &opts); + if (error != GMIO_ERROR_OK) + std::cout << "gmio error: 0x" << std::hex << error << std::endl; +} + +static void stla_write(const void* filepath) +{ + stl_write_generic( + static_cast(filepath), GMIO_STL_FORMAT_ASCII); +} + +static void stlb_write_le(const void* filepath) +{ + stl_write_generic( + static_cast(filepath), GMIO_STL_FORMAT_BINARY_LE); +} + +static void stlb_write_be(const void* filepath) +{ + stl_write_generic( + static_cast(filepath), GMIO_STL_FORMAT_BINARY_BE); +} + +} // namespace BmkGmio + +static void bmk_init() +{ + BmkLib3MF::globalMesh.clear(); + BmkGmio::globalMeshHelper.mesh.clear(); + BmkGmio::globalMeshHelper.vectorTree = NMR::CVectorTree(); + BmkGmio::globalMeshHelper.matrix = nullptr; + BmkGmio::globalMeshHelper.ignoreInvalidFaces = true; +} + +int main(int argc, char** argv) +{ + if (argc > 1) { + const char* filepath = argv[1]; + std::cout << std::endl + << "gmio v" << GMIO_VERSION_STR << std::endl + << "Lib3MF commit a466df47231" << std::endl + << std::endl + << "Input file: " << filepath << std::endl; + + /* Declare benchmarks */ + const benchmark_cmp_arg cmp_args[] = { + { "read", + BmkGmio::stl_read, filepath, + BmkLib3MF::import, filepath }, + { "write(ascii)", + BmkGmio::stla_write, "__bmk_lib3mf_gmio.stla", + nullptr, nullptr }, + { "write(binary/le)", + BmkGmio::stlb_write_le, "__bmk_lib3mf_gmio.stlb_le", + BmkLib3MF::export_binary, "__bmk_lib3mf.stlb_le" }, + { "write(binary/be)", + BmkGmio::stlb_write_be, "__bmk_lib3mf_gmio.stlb_be", + nullptr, nullptr }, + {} + }; + + /* Execute benchmarks */ + std::vector cmp_res_vec; + cmp_res_vec.resize(GMIO_ARRAY_SIZE(cmp_args) - 1); + benchmark_cmp_batch( + 5, cmp_args, &cmp_res_vec[0], bmk_init, nullptr); + + /* Print results */ + const benchmark_cmp_result_array res_array = { + &cmp_res_vec.at(0), cmp_res_vec.size() }; + const benchmark_cmp_result_header header = { "gmio", "Lib3MF" }; + benchmark_print_results( + BENCHMARK_PRINT_FORMAT_MARKDOWN, header, res_array); + } + return 0; +}