diff --git a/CMakeLists.txt b/CMakeLists.txt index d6cd0cd..f5f1765 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ include(CheckFunctionExists) include(CheckCSourceCompiles) include(CheckTypeSize) -project(gmio C) +project(gmio) set(GMIO_VERSION_MAJOR 0) set(GMIO_VERSION_MINOR 1) set(GMIO_VERSION_PATCH 0) @@ -36,6 +36,7 @@ endif () # Options option(GMIO_BUILD_STRICT_C90 "Build with strict conformance to C90 standard" OFF) +option(GMIO_BENCHMARKS "Build performance benchmarks for the gmio library" OFF) # Find bit size of the target architecture math(EXPR GMIO_TARGET_ARCH_BIT_SIZE "8 * ${CMAKE_SIZEOF_VOID_P}") @@ -185,9 +186,11 @@ endif() add_subdirectory(src) add_subdirectory(tests) add_subdirectory(doc) +if(GMIO_BENCHMARKS) + add_subdirectory(benchmarks) +endif() # Examples: # cmake ../.. -DCMAKE_INSTALL_PREFIX=../../gcc-linux64 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_DEBUG_POSTFIX=.debug # cmake ../.. -DCMAKE_INSTALL_PREFIX=../../gcc-linux64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_RELEASE_POSTFIX=.release # make VERBOSE=1 or cmake -DCMAKE_VERBOSE_MAKEFILE=TRUE - diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt new file mode 100644 index 0000000..2093baf --- /dev/null +++ b/benchmarks/CMakeLists.txt @@ -0,0 +1,117 @@ +############################################################################# +## 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". +############################################################################# + +# Options +option(GMIO_BENCHMARKS_ASSIMP "Build benchmark for Assimp" ON) +option(GMIO_BENCHMARKS_OPENCASCADE "Build benchmark for OpenCascade" ON) + +set(ASSIMP_DIR ${CMAKE_SOURCE_DIR} CACHE PATH "Directory where the Assimp library resides") +set(OPENCASCADE_DIR ${CMAKE_SOURCE_DIR} CACHE PATH "Directory where the OpenCascade library resides") + +# List source files in commons/ +file(GLOB COMMONS_FILES commons/*) +set(COMMONS_FILES ${COMMONS_FILES}) + +include_directories(${CMAKE_SOURCE_DIR}/src) +include_directories(${CMAKE_BINARY_DIR}/src/gmio_core) # For cmake generated headers +link_directories(${CMAKE_BINARY_DIR}/src) +find_library(GMIO_LIBPATH gmio HINTS ${CMAKE_BINARY_DIR}/src) +get_filename_component(GMIO_LIB_BASENAME ${GMIO_LIBPATH} NAME_WE) +if ("${GMIO_LIB_BASENAME}" STREQUAL "") + set(GMIO_LIB_BASENAME "gmio") +endif() +link_libraries(${GMIO_LIB_BASENAME}) +if(CMAKE_COMPILER_IS_GNUCC) + link_libraries(m) # -lm +endif() + +add_executable(bench_gmio bench_gmio/main.c ${COMMONS_FILES}) + +function(get_msvc_vernum outVerNum) + if(MSVC60) + set(${outVerNum} 6 PARENT_SCOPE) + elseif(MSVC70) + set(${outVerNum} 7 PARENT_SCOPE) + elseif(MSVC80) + set(${outVerNum} 8 PARENT_SCOPE) + elseif(MSVC90) + set(${outVerNum} 9 PARENT_SCOPE) + elseif(MSVC10) + set(${outVerNum} 10 PARENT_SCOPE) + elseif(MSVC11) + set(${outVerNum} 11 PARENT_SCOPE) + elseif(MSVC12) + set(${outVerNum} 12 PARENT_SCOPE) + endif() +endfunction() + +if(GMIO_BENCHMARKS_ASSIMP) + add_executable(bench_assimp bench_assimp/main.cpp ${COMMONS_FILES}) + # Note: we could use target_include_directories() but it's available in cmake > v2.8.10 + set_property( + TARGET bench_assimp + APPEND PROPERTY INCLUDE_DIRECTORIES ${ASSIMP_DIR}/include) + # Libs + find_library(LIBPATH_ASSIMP assimp ${ASSIMP_DIR}/lib64) + target_link_libraries(bench_assimp ${LIBPATH_ASSIMP}) +endif() + +if(GMIO_BENCHMARKS_OPENCASCADE) + add_executable(bench_occ + bench_occ/main.cpp + ${CMAKE_SOURCE_DIR}/src/gmio_support/stl_occ.cpp + ${COMMONS_FILES}) + + # TODO: define only if target arch is 64b + # Note: we could use target_compile_definitions() but it's available in cmake > v2.8.10 + set_property( + TARGET bench_occ + APPEND PROPERTY COMPILE_DEFINITIONS _OCC64) + + if(WIN32) + set_property( + TARGET bench_occ + APPEND PROPERTY COMPILE_DEFINITIONS WNT) + elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set_property( + TARGET bench_occ + APPEND PROPERTY COMPILE_DEFINITIONS + HAVE_CONFIG_H HAVE_FSTREAM HAVE_IOSTREAM HAVE_IOMANIP HAVE_LIMITS_H) + endif() + + # Note: we could use target_include_directories() but it's available in cmake > v2.8.10 + set_property( + TARGET bench_occ + APPEND PROPERTY INCLUDE_DIRECTORIES ${OPENCASCADE_DIR}/inc) + + # Libs + if(MSVC) + set(_MSVC_VERNUM_ 0) # Init + get_msvc_vernum(_MSVC_VERNUM_) + set(OPENCASCADE_LIBDIR ${OPENCASCADE_DIR}/win64/vc${_MSVC_VERNUM_}/lib) + elseif((${CMAKE_SYSTEM_NAME} MATCHES "Linux") AND CMAKE_COMPILER_IS_GNUCXX) + set(OPENCASCADE_LIBDIR ${OPENCASCADE_DIR}/lin64/gcc/lib) + endif() + message(STATUS ${OPENCASCADE_LIBDIR}) + + find_library(LIBPATH_OPENCASCADE_TKERNEL TKernel ${OPENCASCADE_LIBDIR}) + find_library(LIBPATH_OPENCASCADE_TKMATH TKMath ${OPENCASCADE_LIBDIR}) + find_library(LIBPATH_OPENCASCADE_TKSTL TKSTL ${OPENCASCADE_LIBDIR}) + target_link_libraries( + bench_occ + ${LIBPATH_OPENCASCADE_TKERNEL} + ${LIBPATH_OPENCASCADE_TKMATH} + ${LIBPATH_OPENCASCADE_TKSTL}) +endif() diff --git a/benchmarks/bench_assimp/main.cpp b/benchmarks/bench_assimp/main.cpp new file mode 100644 index 0000000..b1e4518 --- /dev/null +++ b/benchmarks/bench_assimp/main.cpp @@ -0,0 +1,365 @@ +/**************************************************************************** +** 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/bench_tools.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include + +static unsigned totalTriangleCount(const aiScene* scene) +{ + unsigned int meshnum = 0; + for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { + for (unsigned int j = 0; j < scene->mMeshes[i]->mNumFaces; ++j) + ++meshnum; + } + return meshnum; +} + +GMIO_INLINE void copy_gmio_stl_coords( + aiVector3D* vec3, const gmio_stl_coords_t& coords) +{ + *vec3 = *((aiVector3D*)&coords); +} + +GMIO_INLINE void copy_aiVector3D( + gmio_stl_coords_t* coords, const aiVector3D& vec3) +{ + *coords = *((gmio_stl_coords_t*)&vec3); +} + +namespace BenchmarkAssimp { + +Assimp::Importer* globalImporter = NULL; +const aiScene* globalScene = NULL; + +static void bmk_import(const char* filepath) +{ + Assimp::Importer* importer = globalImporter; + const aiScene* scene = importer->ReadFile(filepath, 0); + if (aiGetErrorString() != NULL) + std::cerr << aiGetErrorString() << std::endl; + if (scene == NULL || scene->mNumMeshes <= 0) { + std::cerr << "Failed to read file " << filepath << std::endl; + } + globalScene = scene; + std::cout << "BenchAssimp, triCount = " + << totalTriangleCount(scene) << std::endl; +} + +static void bmk_export_stla(const char* filepath) +{ + Assimp::Exporter exporter; +// for (std::size_t i = 0; i < exporter.GetExportFormatCount(); ++i) { +// std::cout << exporter.GetExportFormatDescription(i)->id << " " +// << exporter.GetExportFormatDescription(i)->description +// << std::endl; +// } + exporter.Export(globalScene, "stl", filepath); +} + +static void bmk_export_stlb(const char* filepath) +{ + Assimp::Exporter exporter; + exporter.Export(globalScene, "stlb", filepath); +} + +static void bmk_main(const char* filepath) +{ + BenchmarkAssimp::globalImporter = new Assimp::Importer; + benchmark(BenchmarkAssimp::bmk_import, + "Assimp::Importer::ReadFile()", + filepath); + benchmark(BenchmarkAssimp::bmk_export_stla, + "Assimp::Exporter::Export(STL_ASCII)", + "__file_bench_assimp.stla"); + benchmark(BenchmarkAssimp::bmk_export_stlb, + "Assimp::Exporter::Export(STL_BINARY)", + "__file_bench_assimp.stlb"); + + delete BenchmarkAssimp::globalImporter; + BenchmarkAssimp::globalImporter = NULL; +} + +} // namespace BenchAssimp + +namespace BenchmarkGmio { + +struct aiSceneHelper +{ + aiScene* scene; + uint32_t totalTriangleCount; + int hasToCountTriangle; +}; + +aiSceneHelper globalSceneHelper = { 0 }; + +static void allocate_stl_scene(aiScene* pScene) +{ + // allocate one mesh + pScene->mNumMeshes = 1; + pScene->mMeshes = new aiMesh*[1]; + aiMesh* pMesh = pScene->mMeshes[0] = new aiMesh(); + pMesh->mMaterialIndex = 0; + + // allocate a single node + pScene->mRootNode = new aiNode(); + pScene->mRootNode->mNumMeshes = 1; + pScene->mRootNode->mMeshes = new unsigned int[1]; + pScene->mRootNode->mMeshes[0] = 0; +} + +static void func_ascii_begin_solid( + void* cookie, size_t stream_size, const char* solid_name) +{ + aiSceneHelper* helper = (aiSceneHelper*)cookie; + helper->hasToCountTriangle = 1; // true + aiScene* pScene = helper->scene; + allocate_stl_scene(pScene); + aiMesh* pMesh = pScene->mMeshes[0]; + + std::strcpy(pScene->mRootNode->mName.data, solid_name); + pScene->mRootNode->mName.length = std::strlen(solid_name); + + // try to guess how many vertices we could have + // assume we'll need 200 bytes for each face + const unsigned facetSize = 200u; + pMesh->mNumFaces = + std::max(1u, static_cast(stream_size) / facetSize); + pMesh->mNumVertices = pMesh->mNumFaces * 3; + pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; +} + +static void binary_begin_solid( + void* cookie, uint32_t tri_count, const gmio_stlb_header_t* /*header*/) +{ + aiSceneHelper* helper = (aiSceneHelper*)cookie; + helper->hasToCountTriangle = 0; // false + aiScene* pScene = helper->scene; + allocate_stl_scene(pScene); + aiMesh* pMesh = pScene->mMeshes[0]; + + pScene->mRootNode->mName.Set(""); + pMesh->mNumFaces = tri_count; + + pMesh->mNumVertices = pMesh->mNumFaces*3; + pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; +} + +static void add_triangle( + void* cookie, uint32_t tri_id, const gmio_stl_triangle_t* triangle) +{ + aiSceneHelper* helper = (aiSceneHelper*)cookie; + aiScene* pScene = helper->scene; + aiMesh* pMesh = pScene->mMeshes[0]; + if (pMesh->mNumFaces <= tri_id) { + std::cout << "add_triangle() reallocate" << std::endl; + // need to resize the arrays, our size estimate was wrong +#if 0 + unsigned int iNeededSize = (unsigned int)(sz-mBuffer) / pMesh->mNumFaces; + if (iNeededSize <= 160) + iNeededSize >>= 1; // prevent endless looping + unsigned int add = (unsigned int)((mBuffer+fileSize)-sz) / iNeededSize; +#endif + + unsigned int add = pMesh->mNumFaces; + add += add >> 3; // add 12.5% as buffer + const unsigned int iNeededSize = (pMesh->mNumFaces + add)*3; + + aiVector3D* pv = new aiVector3D[iNeededSize]; + memcpy(pv, pMesh->mVertices, pMesh->mNumVertices*sizeof(aiVector3D)); + delete[] pMesh->mVertices; + pMesh->mVertices = pv; + pv = new aiVector3D[iNeededSize]; + memcpy(pv, pMesh->mNormals, pMesh->mNumVertices*sizeof(aiVector3D)); + delete[] pMesh->mNormals; + pMesh->mNormals = pv; + + pMesh->mNumVertices = iNeededSize; + pMesh->mNumFaces += add; + } + + aiVector3D* vp = &pMesh->mVertices[tri_id * 3]; + aiVector3D* vn = &pMesh->mNormals[tri_id * 3]; + + copy_gmio_stl_coords(vn, triangle->normal); + *(vn+1) = *vn; + *(vn+2) = *vn; + + copy_gmio_stl_coords(vp, triangle->v1); + copy_gmio_stl_coords(vp+1, triangle->v2); + copy_gmio_stl_coords(vp+2, triangle->v3); + + if (helper->hasToCountTriangle) + ++(helper->totalTriangleCount); +} + +static void end_solid(void* cookie) +{ + aiSceneHelper* helper = (aiSceneHelper*)cookie; + aiScene* pScene = helper->scene; + aiMesh* pMesh = pScene->mMeshes[0]; + + if (helper->hasToCountTriangle) { + pMesh->mNumFaces = helper->totalTriangleCount; + pMesh->mNumVertices = helper->totalTriangleCount * 3; + } + + // now copy faces + pMesh->mFaces = new aiFace[pMesh->mNumFaces]; + for (unsigned int i = 0, p = 0; i < pMesh->mNumFaces;++i) { + aiFace& face = pMesh->mFaces[i]; + face.mIndices = new unsigned int[face.mNumIndices = 3]; + for (unsigned int o = 0; o < 3;++o,++p) { + face.mIndices[o] = p; + } + } + + // create a single default material, using a light gray diffuse color for + // consistency with other geometric types (e.g., PLY). + aiMaterial* pcMat = new aiMaterial; + aiString s; + s.Set(AI_DEFAULT_MATERIAL_NAME); + pcMat->AddProperty(&s, AI_MATKEY_NAME); + + aiColor4D clrDiffuse(0.6f,0.6f,0.6f,1.0f); + pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_DIFFUSE); + pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_SPECULAR); + clrDiffuse = aiColor4D(0.05f,0.05f,0.05f,1.0f); + pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_AMBIENT); + + pScene->mNumMaterials = 1; + pScene->mMaterials = new aiMaterial*[1]; + pScene->mMaterials[0] = pcMat; +} + +static void get_triangle( + const void* cookie, uint32_t tri_id, gmio_stl_triangle_t* triangle) +{ + const aiMesh* mesh = (const aiMesh*)cookie; + const aiFace& f = mesh->mFaces[tri_id]; + + // we need per-face normals. We specified aiProcess_GenNormals as + // pre-requisite for this exporter, but nonetheless we have to expect + // per-vertex normals. + aiVector3D nor; + if (mesh->mNormals) { + for (unsigned int a = 0; a < f.mNumIndices; ++a) { + nor += mesh->mNormals[f.mIndices[a]]; + } + nor.Normalize(); + } + copy_aiVector3D(&triangle->normal, nor); +#if 0 + copy_aiVector3D(&triangle->normal, mesh->mNormals[f.mIndices[0]]); +#endif + + copy_aiVector3D(&triangle->v1, mesh->mVertices[f.mIndices[0]]); + copy_aiVector3D(&triangle->v2, mesh->mVertices[f.mIndices[1]]); + copy_aiVector3D(&triangle->v3, mesh->mVertices[f.mIndices[2]]); +} + +static void bmk_stl_read(const char* filepath) +{ + gmio_stl_mesh_creator_t mesh_creator = { 0 }; + mesh_creator.cookie = &globalSceneHelper; + mesh_creator.func_ascii_begin_solid = func_ascii_begin_solid; + mesh_creator.func_binary_begin_solid = binary_begin_solid; + mesh_creator.func_add_triangle = add_triangle; + mesh_creator.func_end_solid = end_solid; + + const int error = gmio_stl_read_file(filepath, &mesh_creator, NULL); + if (error != GMIO_ERROR_OK) + printf("gmio error: 0x%X\n", error); + + const aiScene* scene = globalSceneHelper.scene; + std::cout << "BenchGmio, triCount = " + << totalTriangleCount(scene) << std::endl; +} + +static void bmk_stl_write(const char* filepath, gmio_stl_format_t format) +{ + const aiMesh* sceneMesh = globalSceneHelper.scene->mMeshes[0]; + + gmio_stl_mesh_t mesh = { 0 }; + mesh.cookie = sceneMesh; + mesh.triangle_count = sceneMesh->mNumFaces; + mesh.func_get_triangle = get_triangle; + + gmio_stl_write_options_t opts = { 0 }; + opts.stla_float32_format = GMIO_FLOAT_TEXT_FORMAT_SHORTEST_UPPERCASE; + opts.stla_float32_prec = 7; + const int error = gmio_stl_write_file(format, filepath, &mesh, NULL, &opts); + if (error != GMIO_ERROR_OK) + printf("gmio error: 0x%X\n", error); +} + +static void bmk_stla_write(const char* filepath) +{ + bmk_stl_write(filepath, GMIO_STL_FORMAT_ASCII); +} + +static void bmk_stlb_write_le(const char* filepath) +{ + bmk_stl_write(filepath, GMIO_STL_FORMAT_BINARY_LE); +} + +static void bmk_stlb_write_be(const char* filepath) +{ + bmk_stl_write(filepath, GMIO_STL_FORMAT_BINARY_BE); +} + +static void bmk_main(const char* filepath) +{ + BenchmarkGmio::globalSceneHelper.scene = new aiScene; + BenchmarkGmio::globalSceneHelper.totalTriangleCount = 0; + + benchmark(BenchmarkGmio::bmk_stl_read, + "gmio_stl_read()", + filepath); + benchmark(BenchmarkGmio::bmk_stla_write, + "gmio_stl_write(STL_ASCII)", + "__file_bench_gmio.stla"); + benchmark(BenchmarkGmio::bmk_stlb_write_le, + "gmio_stl_write(STL_BINARY_LE)", + "__file_bench_gmio_le.stlb"); + benchmark(BenchmarkGmio::bmk_stlb_write_be, + "gmio_stl_write(STL_BINARY_BE)", + "__file_bench_gmio_be.stlb"); + + delete BenchmarkGmio::globalSceneHelper.scene; + BenchmarkGmio::globalSceneHelper.scene = NULL; +} + +} // namespace BenchmarkGmio + +int main(int argc, char** argv) +{ + if (argc > 1) { + benchmark_forward_list(BenchmarkAssimp::bmk_main, argc - 1, argv + 1); + benchmark_forward_list(BenchmarkGmio::bmk_main, argc - 1, argv + 1); + } + return 0; +} diff --git a/benchmarks/bench_gmio/main.c b/benchmarks/bench_gmio/main.c new file mode 100644 index 0000000..82b0b73 --- /dev/null +++ b/benchmarks/bench_gmio/main.c @@ -0,0 +1,209 @@ +/**************************************************************************** +** 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 +#include + +#include "../commons/bench_tools.h" + +#include + +typedef struct my_igeom +{ + uint32_t facet_count; +} my_igeom_t; + +static void dummy_process_triangle( + void* cookie, + uint32_t triangle_id, + const gmio_stl_triangle_t* triangle) +{ + my_igeom_t* my_igeom = (my_igeom_t*)(cookie); + if (my_igeom != NULL) + ++(my_igeom->facet_count); +} + +static void bench_gmio_stl_read(const char* filepath) +{ + my_igeom_t cookie = { 0 }; + gmio_stl_mesh_creator_t mesh_creator = { 0 }; + int error = GMIO_ERROR_OK; + + mesh_creator.cookie = &cookie; + mesh_creator.func_add_triangle = dummy_process_triangle; + error = gmio_stl_read_file(filepath, &mesh_creator, NULL); + if (error != GMIO_ERROR_OK) + printf("gmio error: 0x%X\n", error); +} + +static gmio_endianness_t to_byte_order(gmio_stl_format_t format) +{ + switch (format) { + case GMIO_STL_FORMAT_BINARY_LE: + return GMIO_ENDIANNESS_LITTLE; + case GMIO_STL_FORMAT_BINARY_BE: + return GMIO_ENDIANNESS_BIG; + case GMIO_STL_FORMAT_ASCII: + case GMIO_STL_FORMAT_UNKNOWN: + return GMIO_ENDIANNESS_HOST; + } + return GMIO_ENDIANNESS_UNKNOWN; +} + +enum { STL_TRIANGLE_ARRAY_SIZE = 512 }; +typedef struct stl_readwrite_conv +{ + gmio_transfer_t trsf; + gmio_stl_format_t in_format; + gmio_stl_format_t out_format; + gmio_stl_triangle_t triangle_array[STL_TRIANGLE_ARRAY_SIZE]; + unsigned triangle_pos; + uint32_t total_triangle_count; +} stl_readwrite_conv_t; + +static void readwrite_ascii_begin_solid( + void* cookie, size_t stream_size, const char* solid_name) +{ + stl_readwrite_conv_t* rw_conv = (stl_readwrite_conv_t*)cookie; + gmio_stream_t* stream = &rw_conv->trsf.stream; + if (rw_conv->out_format == GMIO_STL_FORMAT_ASCII) { + stream->func_write(stream->cookie, "solid ", 1, 6); + stream->func_write(stream->cookie, solid_name, 1, strlen(solid_name)); + stream->func_write(stream->cookie, "\n", 1, 1); + } + else { + /* For binary STL, facet count <- 0 because it cannot be known at this + * point. Header will be correctly rewritten at the end of the read + * procedure (in gmio_stl_mesh_creator::func_end_solid() callback) + */ + const gmio_endianness_t byte_order = to_byte_order(rw_conv->out_format); + gmio_stlb_write_header(stream, byte_order, NULL, 0); + } +} + +static void readwrite_binary_begin_solid( + void* cookie, uint32_t tri_count, const gmio_stlb_header_t* header) +{ + stl_readwrite_conv_t* rw_conv = (stl_readwrite_conv_t*)cookie; + gmio_stream_t* stream = &rw_conv->trsf.stream; + if (rw_conv->out_format == GMIO_STL_FORMAT_ASCII) { + stream->func_write(stream->cookie, "solid\n", 1, 6); + } + else { + const gmio_endianness_t byte_order = to_byte_order(rw_conv->out_format); + gmio_stlb_write_header(stream, byte_order, header, tri_count); + } +} + +static void readwrite_get_triangle( + const void* cookie, uint32_t tri_id, gmio_stl_triangle_t* triangle) +{ + const gmio_stl_triangle_t* tri_array = (const gmio_stl_triangle_t*)cookie; + *triangle = tri_array[tri_id]; +} + +static void stl_readwrite_flush_triangles(stl_readwrite_conv_t* rw_conv) +{ + gmio_stl_mesh_t mesh = { 0 }; + gmio_stl_write_options_t options = { 0 }; + mesh.cookie = &rw_conv->triangle_array[0]; + mesh.triangle_count = rw_conv->triangle_pos; + mesh.func_get_triangle = &readwrite_get_triangle; + options.stl_write_triangles_only = GMIO_TRUE; + options.stla_float32_format = GMIO_FLOAT_TEXT_FORMAT_SCIENTIFIC_LOWERCASE; + options.stla_float32_prec = 6; + gmio_stl_write(rw_conv->out_format, &rw_conv->trsf, &mesh, &options); + rw_conv->triangle_pos = 0; +} + +static void readwrite_add_triangle( + void* cookie, uint32_t tri_id, const gmio_stl_triangle_t* triangle) +{ + stl_readwrite_conv_t* rw_conv = (stl_readwrite_conv_t*)cookie; + if (rw_conv->triangle_pos >= STL_TRIANGLE_ARRAY_SIZE) + stl_readwrite_flush_triangles(rw_conv); + rw_conv->triangle_array[rw_conv->triangle_pos] = *triangle; + ++(rw_conv->triangle_pos); + rw_conv->total_triangle_count = tri_id + 1; +} + +static void readwrite_end_solid(void* cookie) +{ + stl_readwrite_conv_t* rw_conv = (stl_readwrite_conv_t*)cookie; + gmio_stream_t* stream = &rw_conv->trsf.stream; + if (rw_conv->triangle_pos != 0) + stl_readwrite_flush_triangles(rw_conv); + if (rw_conv->out_format == GMIO_STL_FORMAT_ASCII) { + stream->func_write(stream->cookie, "endsolid", 1, 8); + } + else if (rw_conv->in_format == GMIO_STL_FORMAT_ASCII) { + const gmio_endianness_t byte_order = to_byte_order(rw_conv->out_format); + /* The total facet count has to be written because it wasn't known at + * the beginning of the read procedure */ + stream->func_rewind(stream->cookie); + gmio_stlb_write_header( + stream, byte_order, NULL, rw_conv->total_triangle_count); + } +} + +static void bench_gmio_stl_readwrite_conv(const char* filepath) +{ + FILE* infile = fopen(filepath, "rb"); + FILE* outfile = fopen("_readwrite_conv.stl", "wb"); + gmio_transfer_t in_trsf = { 0 }; + stl_readwrite_conv_t rw_conv = { 0 }; + gmio_stl_mesh_creator_t mesh_creator = { 0 }; + int error = GMIO_ERROR_OK; + + /* rw_conv.out_format = GMIO_STL_FORMAT_BINARY_LE; */ + rw_conv.out_format = GMIO_STL_FORMAT_ASCII; + + if (infile != NULL) { + in_trsf.buffer = gmio_buffer_malloc(512 * 1024); + in_trsf.stream = gmio_stream_stdio(infile); + rw_conv.in_format = gmio_stl_get_format(&in_trsf.stream); + } + if (outfile != NULL) { + rw_conv.trsf.buffer = gmio_buffer_malloc(512 * 1024); + rw_conv.trsf.stream = gmio_stream_stdio(outfile); + } + + mesh_creator.cookie = &rw_conv; + mesh_creator.func_ascii_begin_solid = &readwrite_ascii_begin_solid; + mesh_creator.func_binary_begin_solid = &readwrite_binary_begin_solid; + mesh_creator.func_add_triangle = &readwrite_add_triangle; + mesh_creator.func_end_solid = &readwrite_end_solid; + + error = gmio_stl_read(&in_trsf, &mesh_creator); + + gmio_buffer_deallocate(&in_trsf.buffer); + gmio_buffer_deallocate(&rw_conv.trsf.buffer); + + if (error != GMIO_ERROR_OK) + printf("gmio error: 0x%X\n", error); +} + +int main(int argc, char** argv) +{ + if (argc > 1) { + benchmark_list(&bench_gmio_stl_read, + "gmio_stl_read_file()", + argc - 1, argv + 1); + benchmark_list(&bench_gmio_stl_readwrite_conv, + "gmio_stl_readwrite_conv()", + argc - 1, argv + 1); + } + return 0; +} diff --git a/benchmarks/bench_occ/main.cpp b/benchmarks/bench_occ/main.cpp new file mode 100644 index 0000000..9f571f0 --- /dev/null +++ b/benchmarks/bench_occ/main.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** +** 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 +#include +#include + +#include +#include +#include + +#include "../commons/bench_tools.h" + +namespace BenchmarkOcc { + +Handle_StlMesh_Mesh stlMesh; + +static void bmk_RWStl_ReadFile(const char* filepath) +{ + stlMesh = RWStl::ReadFile(OSD_Path(filepath)); + if (stlMesh.IsNull()) + printf("RWStl::ReadFile(): null mesh\n"); +} + +static void bmk_RWStl_WriteAscii(const char* filepath) +{ + if (!RWStl::WriteAscii(stlMesh, OSD_Path(filepath))) + printf("RWStl::WriteAscii() failure\n"); +} + +static void bmk_RWStl_WriteBinary(const char* filepath) +{ + if (!RWStl::WriteBinary(stlMesh, OSD_Path(filepath))) + printf("RWStl::WriteBinary() failure\n"); +} + +static void bmk_main(const char* filepath) +{ + benchmark(BenchmarkOcc::bmk_RWStl_ReadFile, + "RWStl::ReadFile()", + filepath); + benchmark(BenchmarkOcc::bmk_RWStl_WriteAscii, + "RWStl::WriteAscii", + "__file_bench_occ.stla"); + benchmark(BenchmarkOcc::bmk_RWStl_WriteBinary, + "RWStl::WriteBinary", + "__file_bench_occ.stlb"); +} + +} // namespace BenchmarkOcc + +namespace BenchmarkGmio { + +Handle_StlMesh_Mesh stlMesh; + +static void bmk_stl_read(const char* filepath) +{ + stlMesh = new StlMesh_Mesh; + gmio_stl_mesh_creator_t mesh_creator = gmio_stl_occmesh_creator(stlMesh); + int error = gmio_stl_read_file(filepath, &mesh_creator, NULL); + if (error != GMIO_ERROR_OK) + printf("gmio error: 0x%X\n", error); +} + +static void bmk_stl_write(const char* filepath, gmio_stl_format_t format) +{ + const gmio_OccStlMeshDomain occMeshDomain(stlMesh); + const gmio_stl_mesh_t mesh = gmio_stl_occmesh(occMeshDomain); + + gmio_stl_write_options_t opts = { 0 }; + opts.stla_float32_format = GMIO_FLOAT_TEXT_FORMAT_SHORTEST_UPPERCASE; + opts.stla_float32_prec = 7; + const int error = gmio_stl_write_file(format, filepath, &mesh, NULL, &opts); + if (error != GMIO_ERROR_OK) + printf("gmio error: 0x%X\n", error); +} + +static void bmk_stla_write(const char* filepath) +{ + bmk_stl_write(filepath, GMIO_STL_FORMAT_ASCII); +} + +static void bmk_stlb_write_le(const char* filepath) +{ + bmk_stl_write(filepath, GMIO_STL_FORMAT_BINARY_LE); +} + +static void bmk_stlb_write_be(const char* filepath) +{ + bmk_stl_write(filepath, GMIO_STL_FORMAT_BINARY_BE); +} + +static void bmk_main(const char* filepath) +{ + benchmark(bmk_stl_read, + "gmio_stl_read()", + filepath); + benchmark(bmk_stla_write, + "gmio_stl_write(STL_ASCII)", + "__file_bench_gmio.stla"); + benchmark(bmk_stlb_write_le, + "gmio_stl_write(STL_BINARY_LE)", + "__file_bench_gmio_le.stlb"); + benchmark(bmk_stlb_write_be, + "gmio_stl_write(STL_BINARY_BE)", + "__file_bench_gmio_be.stlb"); +} +} // namespace BenchmarkGmio + +int main(int argc, char** argv) +{ + if (argc > 1) { + benchmark_forward_list(BenchmarkOcc::bmk_main, argc - 1, argv + 1); + benchmark_forward_list(BenchmarkGmio::bmk_main, argc - 1, argv + 1); + } + + return 0; +} diff --git a/benchmarks/commons/bench_tools.c b/benchmarks/commons/bench_tools.c new file mode 100644 index 0000000..1249a7f --- /dev/null +++ b/benchmarks/commons/bench_tools.c @@ -0,0 +1,106 @@ +/**************************************************************************** +** 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 "bench_tools.h" + +#include + +#ifdef WIN32 +# include +# define BENCH_TIMER_WINDOWS +#else +# define BENCH_TIMER_LIBC +#endif + +typedef struct bench_timer +{ +#ifdef BENCH_TIMER_WINDOWS + LARGE_INTEGER start_time; + LARGE_INTEGER frequency; +#elif defined(BENCH_TIMER_LIBC) + clock_t start_tick; +#endif +} bench_timer_t; + +void bench_timer_start(bench_timer_t* timer) +{ +#ifdef BENCH_TIMER_WINDOWS + QueryPerformanceFrequency(&timer->frequency); + QueryPerformanceCounter(&timer->start_time); +#elif defined(BENCH_TIMER_LIBC) + timer->start_tick = clock(); +#endif +} + +int64_t bench_timer_elapsed_ms(const bench_timer_t* timer) +{ +#ifdef BENCH_TIMER_WINDOWS + LARGE_INTEGER end_time = { 0 }; + LARGE_INTEGER elapsed = { 0 }; + QueryPerformanceCounter(&end_time); + elapsed.QuadPart = end_time.QuadPart - timer->start_time.QuadPart; + + /* + We now have the elapsed number of ticks, along with the + number of ticks-per-second. We use these values + to convert to the number of elapsed microseconds. + To guard against loss-of-precision, we convert + to milliseconds *before* dividing by ticks-per-second. + */ + + elapsed.QuadPart *= 1000; + elapsed.QuadPart /= timer->frequency.QuadPart; + return elapsed.QuadPart; +#elif defined(BENCH_TIMER_LIBC) + /* For seconds: + * return (float)((clock() - start_tick) / (float)CLOCKS_PER_SEC); */ + return clock() - timer->start_tick; +#endif +} + +void benchmark_list( + bench_file_func_t func, const char* title, int argc, char **argv) +{ + bench_timer_t timer = { 0 }; + int iarg; + + if (func == NULL) + return; + + bench_timer_start(&timer); + + printf("Bench %s ...\n", title); + for (iarg = 0; iarg < argc; ++iarg) { + printf(" File %s ...\n", argv[iarg]); + (*func)(argv[iarg]); + } + /*printf(" exec time: %.2fs\n\n", elapsed_secs(start_tick));*/ + printf(" exec time: %lldms\n\n", bench_timer_elapsed_ms(&timer)); +} + +void benchmark_forward_list(bench_file_func_t func, int argc, char **argv) +{ + int i = 0; + while (i < argc) { + func(argv[i]); + ++i; + } +} + +void benchmark( + bench_file_func_t func, const char *title, const char* filepath) +{ + benchmark_list(func, title, 1, (char**)&filepath); +} diff --git a/benchmarks/commons/bench_tools.h b/benchmarks/commons/bench_tools.h new file mode 100644 index 0000000..faf5456 --- /dev/null +++ b/benchmarks/commons/bench_tools.h @@ -0,0 +1,36 @@ +/**************************************************************************** +** 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". +****************************************************************************/ + +#ifndef BENCH_TOOLS_H +#define BENCH_TOOLS_H + +#include + +GMIO_C_LINKAGE_BEGIN + +typedef void (*bench_file_func_t)(const char*); + +void benchmark_list( + bench_file_func_t func, const char* title, int argc, char** argv); + +void benchmark_forward_list( + bench_file_func_t func, int argc, char** argv); + +void benchmark( + bench_file_func_t func, const char* title, const char* filepath); + +GMIO_C_LINKAGE_END + +#endif /* BENCH_TOOLS_H */