diff --git a/src/RWGltf/RWGltf_CafWriter.cxx b/src/RWGltf/RWGltf_CafWriter.cxx index b5776fd882..e6bcdbefc0 100644 --- a/src/RWGltf/RWGltf_CafWriter.cxx +++ b/src/RWGltf/RWGltf_CafWriter.cxx @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -170,6 +171,72 @@ namespace #endif } +#ifdef HAVE_DRACO +//! Functor for parallel execution of encoding meshes to Draco buffers. +class DracoEncodingFunctor +{ +public: + + DracoEncodingFunctor (const Message_ProgressRange& theProgress, + draco::Encoder& theDracoEncoder, + const std::vector>& theMeshes, + std::vector>& theEncoderBuffers) + : myProgress(theProgress, "Draco compression", Max(1, int(theMeshes.size()))), + myDracoEncoder(&theDracoEncoder), + myRanges(0, int(theMeshes.size()) - 1), + myMeshes(&theMeshes), + myEncoderBuffers(&theEncoderBuffers) + { + for (int anIndex = 0; anIndex != int(theMeshes.size()); ++anIndex) + { + myRanges.SetValue(anIndex, myProgress.Next()); + } + } + + void operator () (int theMeshIndex) const + { + const std::shared_ptr& aCurrentMesh = myMeshes->at(theMeshIndex); + if (aCurrentMesh->NodesVec.empty()) + { + return; + } + + Message_ProgressScope aScope(myRanges[theMeshIndex], NULL, 1); + + draco::Mesh aMesh; + writeNodesToDracoMesh (aMesh, aCurrentMesh->NodesVec); + + if (!aCurrentMesh->NormalsVec.empty()) + { + writeNormalsToDracoMesh (aMesh, aCurrentMesh->NormalsVec); + } + + if (!aCurrentMesh->TexCoordsVec.empty()) + { + writeTexCoordsToDracoMesh (aMesh, aCurrentMesh->TexCoordsVec); + } + + writeIndicesToDracoMesh (aMesh, aCurrentMesh->IndicesVec); + + std::shared_ptr anEncoderBuffer = std::make_shared(); + draco::Status aStatus = myDracoEncoder->EncodeMeshToBuffer (aMesh, anEncoderBuffer.get()); + if (aStatus.ok()) + { + myEncoderBuffers->at(theMeshIndex) = anEncoderBuffer; + } + + aScope.Next(); + } + +private: + Message_ProgressScope myProgress; + draco::Encoder* myDracoEncoder; + NCollection_Array1 myRanges; + const std::vector>* myMeshes; + std::vector>* myEncoderBuffers; +}; +#endif + //================================================================ // Function : Constructor // Purpose : @@ -185,7 +252,8 @@ RWGltf_CafWriter::RWGltf_CafWriter (const TCollection_AsciiString& theFile, myToEmbedTexturesInGlb (true), myToMergeFaces (false), myToSplitIndices16 (false), - myBinDataLen64 (0) + myBinDataLen64 (0), + myToParallel (false) { myCSTrsf.SetOutputLengthUnit (1.0); // meters myCSTrsf.SetOutputCoordinateSystem (RWMesh_CoordinateSystem_glTF); @@ -537,6 +605,8 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument myBinDataMap.Clear(); myBinDataLen64 = 0; + Message_ProgressScope aScope(theProgress, "Write binary data", myDracoParameters.DracoCompression ? 2 : 1); + const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem(); std::shared_ptr aBinFile = aFileSystem->OpenOStream (myBinFileNameFull, std::ios::out | std::ios::binary); if (aBinFile.get() == NULL @@ -546,7 +616,7 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument return false; } - Message_ProgressScope aPSentryBin (theProgress, "Binary data", 4); + Message_ProgressScope aPSentryBin (aScope.Next(), "Binary data", 4); const RWGltf_GltfArrayType anArrTypes[4] = { RWGltf_GltfArrayType_Position, @@ -797,7 +867,6 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument #ifdef HAVE_DRACO OSD_Timer aDracoTimer; aDracoTimer.Start(); - int aBuffId = 0; draco::Encoder aDracoEncoder; aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::POSITION, myDracoParameters.QuantizePositionBits); aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::NORMAL, myDracoParameters.QuantizeNormalBits); @@ -805,38 +874,23 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::COLOR, myDracoParameters.QuantizeColorBits); aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::GENERIC, myDracoParameters.QuantizeGenericBits); aDracoEncoder.SetSpeedOptions (myDracoParameters.CompressionLevel, myDracoParameters.CompressionLevel); - for (size_t aMeshInd = 0; aMeshInd != aMeshes.size(); ++aMeshInd) + + std::vector> anEncoderBuffers(aMeshes.size()); + DracoEncodingFunctor aFunctor (aScope.Next(), aDracoEncoder, aMeshes, anEncoderBuffers); + OSD_Parallel::For (0, int(aMeshes.size()), aFunctor, !myToParallel); + + for (size_t aBuffInd = 0; aBuffInd != anEncoderBuffers.size(); ++aBuffInd) { - const std::shared_ptr& aCurrentMesh = aMeshes[aMeshInd]; - if (aCurrentMesh->NodesVec.empty()) + if (anEncoderBuffers.at(aBuffInd).get() == nullptr) { - continue; - } - - draco::Mesh aDracoMesh; - writeNodesToDracoMesh (aDracoMesh, aCurrentMesh->NodesVec); - if (!aCurrentMesh->NormalsVec.empty()) - { - writeNormalsToDracoMesh (aDracoMesh, aCurrentMesh->NormalsVec); - } - if (!aCurrentMesh->TexCoordsVec.empty()) - { - writeTexCoordsToDracoMesh (aDracoMesh, aCurrentMesh->TexCoordsVec); - } - writeIndicesToDracoMesh (aDracoMesh, aCurrentMesh->IndicesVec); - - draco::EncoderBuffer anEncoderBuff; - draco::Status aStatus = aDracoEncoder.EncodeMeshToBuffer (aDracoMesh, &anEncoderBuff); - if (!aStatus.ok()) - { - Message::SendFail (TCollection_AsciiString("Error: mesh cannot be encoded in draco buffer.")); + Message::SendFail(TCollection_AsciiString("Error: mesh not encoded in draco buffer.")); return false; } - RWGltf_GltfBufferView aBuffViewDraco; - aBuffViewDraco.Id = aBuffId++; + aBuffViewDraco.Id = (int)aBuffInd; aBuffViewDraco.ByteOffset = aBinFile->tellp(); - aBinFile->write (anEncoderBuff.data(), std::streamsize(anEncoderBuff.size())); + const draco::EncoderBuffer& anEncoderBuff = *anEncoderBuffers.at(aBuffInd); + aBinFile->write(anEncoderBuff.data(), std::streamsize(anEncoderBuff.size())); if (!aBinFile->good()) { Message::SendFail (TCollection_AsciiString("File '") + myBinFileNameFull + "' cannot be written"); diff --git a/src/RWGltf/RWGltf_CafWriter.hxx b/src/RWGltf/RWGltf_CafWriter.hxx index be1d8a344d..be5d05f637 100644 --- a/src/RWGltf/RWGltf_CafWriter.hxx +++ b/src/RWGltf/RWGltf_CafWriter.hxx @@ -125,6 +125,12 @@ public: //! May reduce binary data size thanks to smaller triangle indexes. void SetSplitIndices16 (bool theToSplit) { myToSplitIndices16 = theToSplit; } + //! Return TRUE if multithreaded optimizations are allowed; FALSE by default. + bool ToParallel() const { return myToParallel; } + + //! Setup multithreaded execution. + void SetParallel (bool theToParallel) { myToParallel = theToParallel; } + //! Return Draco parameters const RWGltf_DracoParameters& CompressionParameters() const { return myDracoParameters; } @@ -397,6 +403,7 @@ protected: int64_t myBinDataLen64; //!< length of binary file std::vector myBuffViewsDraco; //!< vector of buffers view with compression data + Standard_Boolean myToParallel; //!< flag to use multithreading; FALSE by default RWGltf_DracoParameters myDracoParameters; //!< Draco parameters }; diff --git a/src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx b/src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx index f0c2c917e0..62aaad6e2e 100644 --- a/src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx +++ b/src/XSDRAWSTLVRML/XSDRAWSTLVRML.cxx @@ -383,6 +383,7 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI, RWMesh_CoordinateSystem aSystemCoordSys = RWMesh_CoordinateSystem_Zup; bool toForceUVExport = false, toEmbedTexturesInGlb = true; bool toMergeFaces = false, toSplitIndices16 = false; + bool isParallel = false; RWMesh_NameFormat aNodeNameFormat = RWMesh_NameFormat_InstanceOrProduct; RWMesh_NameFormat aMeshNameFormat = RWMesh_NameFormat_Product; RWGltf_DracoParameters aDracoParameters; @@ -556,6 +557,10 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI, { aDracoParameters.UnifiedQuantization = Draw::ParseOnOffIterator(theNbArgs, theArgVec, anArgIter); } + else if (anArgCase == "-parallel") + { + isParallel = Draw::ParseOnOffIterator(theNbArgs, theArgVec, anArgIter); + } else { Message::SendFail() << "Syntax error at '" << theArgVec[anArgIter] << "'"; @@ -587,6 +592,7 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI, aWriter.SetToEmbedTexturesInGlb (toEmbedTexturesInGlb); aWriter.SetMergeFaces (toMergeFaces); aWriter.SetSplitIndices16 (toSplitIndices16); + aWriter.SetParallel(isParallel); aWriter.SetCompressionParameters(aDracoParameters); aWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit (aScaleFactorM); aWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem (aSystemCoordSys); @@ -2450,7 +2456,7 @@ void XSDRAWSTLVRML::InitCommands (Draw_Interpretor& theCommands) "\n\t\t: [-meshNameFormat {empty|product|instance|instOrProd|prodOrInst|prodAndInst|verbose}]=product" "\n\t\t: [-draco]=0 [-compressionLevel {0-10}]=7 [-quantizePositionBits Value]=14 [-quantizeNormalBits Value]=10" "\n\t\t: [-quantizeTexcoordBits Value]=12 [-quantizeColorBits Value]=8 [-quantizeGenericBits Value]=12" - "\n\t\t: [-unifiedQuantization]=0" + "\n\t\t: [-unifiedQuantization]=0 [-parallel]=0" "\n\t\t: Write XDE document into glTF file." "\n\t\t: -trsfFormat preferred transformation format" "\n\t\t: -systemCoordSys system coordinate system; Zup when not specified" @@ -2460,7 +2466,7 @@ void XSDRAWSTLVRML::InitCommands (Draw_Interpretor& theCommands) "\n\t\t: -texturesSeparate write textures to separate files" "\n\t\t: -nodeNameFormat name format for Nodes" "\n\t\t: -meshNameFormat name format for Meshes" - "\n\t\t: -draco use Draco compression 3D geometric meshes" + "\n\t\t: -draco use Draco compression 3D geometric meshes" "\n\t\t: -compressionLevel draco compression level [0-10] (by default 7), a value of 0 will apply sequential encoding and preserve face order" "\n\t\t: -quantizePositionBits quantization bits for position attribute when using Draco compression (by default 14)" "\n\t\t: -quantizeNormalBits quantization bits for normal attribute when using Draco compression (by default 10)" @@ -2468,7 +2474,8 @@ void XSDRAWSTLVRML::InitCommands (Draw_Interpretor& theCommands) "\n\t\t: -quantizeColorBits quantization bits for color attribute when using Draco compression (by default 8)" "\n\t\t: -quantizeGenericBits quantization bits for skinning attribute (joint indices and joint weights)" "\n and custom attributes when using Draco compression (by default 12)" - "\n\t\t: -unifiedQuantization quantization is applied on each primitive separately if this option is false", + "\n\t\t: -unifiedQuantization quantization is applied on each primitive separately if this option is false" + "\n\t\t: -parallel use multithreading for Draco compression", __FILE__, WriteGltf, g); theCommands.Add ("writegltf", "writegltf shape file", diff --git a/tests/de_mesh/gltf_write/bull_parallel b/tests/de_mesh/gltf_write/bull_parallel new file mode 100644 index 0000000000..5e3967bd4d --- /dev/null +++ b/tests/de_mesh/gltf_write/bull_parallel @@ -0,0 +1,13 @@ +puts "========" +puts "0032867: Data Exchange - Implement Draco compression for writing glTF" +puts "Test case exporting model into glb (binary glTF) file." +puts "========" + +Close D0 -silent +ReadGltf D0 [locate_data_file bug32867_bull.glb] + +set aGltfFile1 "${imagedir}/${casename}_tmp1.glb" + +WriteGltf D0 "$aGltfFile1" -draco on -parallel + +ReadGltf D "$aGltfFile1"