1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-04-21 10:13:43 +03:00

0032979: Data Exchange, RWGltf_CafWriter - support multi-threaded Draco compression

'MultiThread' field was added to structure RWGltf_DracoParameters for using multithreading.
Class CafWriter_DracoEncodingFunctor was added for multithreaded compression.
This commit is contained in:
ichesnok 2022-07-26 14:06:58 +03:00 committed by smoskvin
parent 621ed3bc36
commit f74f684b16
4 changed files with 113 additions and 32 deletions

View File

@ -21,6 +21,7 @@
#include <NCollection_DataMap.hxx> #include <NCollection_DataMap.hxx>
#include <OSD_FileSystem.hxx> #include <OSD_FileSystem.hxx>
#include <OSD_File.hxx> #include <OSD_File.hxx>
#include <OSD_Parallel.hxx>
#include <OSD_Path.hxx> #include <OSD_Path.hxx>
#include <OSD_Timer.hxx> #include <OSD_Timer.hxx>
#include <RWGltf_GltfAccessorLayout.hxx> #include <RWGltf_GltfAccessorLayout.hxx>
@ -170,6 +171,72 @@ namespace
#endif #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<std::shared_ptr<RWGltf_CafWriter::Mesh>>& theMeshes,
std::vector<std::shared_ptr<draco::EncoderBuffer>>& 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<RWGltf_CafWriter::Mesh>& 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<draco::EncoderBuffer> anEncoderBuffer = std::make_shared<draco::EncoderBuffer>();
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<Message_ProgressRange> myRanges;
const std::vector<std::shared_ptr<RWGltf_CafWriter::Mesh>>* myMeshes;
std::vector<std::shared_ptr<draco::EncoderBuffer>>* myEncoderBuffers;
};
#endif
//================================================================ //================================================================
// Function : Constructor // Function : Constructor
// Purpose : // Purpose :
@ -185,7 +252,8 @@ RWGltf_CafWriter::RWGltf_CafWriter (const TCollection_AsciiString& theFile,
myToEmbedTexturesInGlb (true), myToEmbedTexturesInGlb (true),
myToMergeFaces (false), myToMergeFaces (false),
myToSplitIndices16 (false), myToSplitIndices16 (false),
myBinDataLen64 (0) myBinDataLen64 (0),
myToParallel (false)
{ {
myCSTrsf.SetOutputLengthUnit (1.0); // meters myCSTrsf.SetOutputLengthUnit (1.0); // meters
myCSTrsf.SetOutputCoordinateSystem (RWMesh_CoordinateSystem_glTF); myCSTrsf.SetOutputCoordinateSystem (RWMesh_CoordinateSystem_glTF);
@ -537,6 +605,8 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
myBinDataMap.Clear(); myBinDataMap.Clear();
myBinDataLen64 = 0; myBinDataLen64 = 0;
Message_ProgressScope aScope(theProgress, "Write binary data", myDracoParameters.DracoCompression ? 2 : 1);
const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem(); const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
std::shared_ptr<std::ostream> aBinFile = aFileSystem->OpenOStream (myBinFileNameFull, std::ios::out | std::ios::binary); std::shared_ptr<std::ostream> aBinFile = aFileSystem->OpenOStream (myBinFileNameFull, std::ios::out | std::ios::binary);
if (aBinFile.get() == NULL if (aBinFile.get() == NULL
@ -546,7 +616,7 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
return false; return false;
} }
Message_ProgressScope aPSentryBin (theProgress, "Binary data", 4); Message_ProgressScope aPSentryBin (aScope.Next(), "Binary data", 4);
const RWGltf_GltfArrayType anArrTypes[4] = const RWGltf_GltfArrayType anArrTypes[4] =
{ {
RWGltf_GltfArrayType_Position, RWGltf_GltfArrayType_Position,
@ -797,7 +867,6 @@ bool RWGltf_CafWriter::writeBinData (const Handle(TDocStd_Document)& theDocument
#ifdef HAVE_DRACO #ifdef HAVE_DRACO
OSD_Timer aDracoTimer; OSD_Timer aDracoTimer;
aDracoTimer.Start(); aDracoTimer.Start();
int aBuffId = 0;
draco::Encoder aDracoEncoder; draco::Encoder aDracoEncoder;
aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::POSITION, myDracoParameters.QuantizePositionBits); aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::POSITION, myDracoParameters.QuantizePositionBits);
aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::NORMAL, myDracoParameters.QuantizeNormalBits); 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::COLOR, myDracoParameters.QuantizeColorBits);
aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::GENERIC, myDracoParameters.QuantizeGenericBits); aDracoEncoder.SetAttributeQuantization (draco::GeometryAttribute::GENERIC, myDracoParameters.QuantizeGenericBits);
aDracoEncoder.SetSpeedOptions (myDracoParameters.CompressionLevel, myDracoParameters.CompressionLevel); aDracoEncoder.SetSpeedOptions (myDracoParameters.CompressionLevel, myDracoParameters.CompressionLevel);
for (size_t aMeshInd = 0; aMeshInd != aMeshes.size(); ++aMeshInd)
{
const std::shared_ptr<RWGltf_CafWriter::Mesh>& aCurrentMesh = aMeshes[aMeshInd];
if (aCurrentMesh->NodesVec.empty())
{
continue;
}
draco::Mesh aDracoMesh; std::vector<std::shared_ptr<draco::EncoderBuffer>> anEncoderBuffers(aMeshes.size());
writeNodesToDracoMesh (aDracoMesh, aCurrentMesh->NodesVec); DracoEncodingFunctor aFunctor (aScope.Next(), aDracoEncoder, aMeshes, anEncoderBuffers);
if (!aCurrentMesh->NormalsVec.empty()) OSD_Parallel::For (0, int(aMeshes.size()), aFunctor, !myToParallel);
{
writeNormalsToDracoMesh (aDracoMesh, aCurrentMesh->NormalsVec);
}
if (!aCurrentMesh->TexCoordsVec.empty())
{
writeTexCoordsToDracoMesh (aDracoMesh, aCurrentMesh->TexCoordsVec);
}
writeIndicesToDracoMesh (aDracoMesh, aCurrentMesh->IndicesVec);
draco::EncoderBuffer anEncoderBuff; for (size_t aBuffInd = 0; aBuffInd != anEncoderBuffers.size(); ++aBuffInd)
draco::Status aStatus = aDracoEncoder.EncodeMeshToBuffer (aDracoMesh, &anEncoderBuff);
if (!aStatus.ok())
{ {
Message::SendFail (TCollection_AsciiString("Error: mesh cannot be encoded in draco buffer.")); if (anEncoderBuffers.at(aBuffInd).get() == nullptr)
{
Message::SendFail(TCollection_AsciiString("Error: mesh not encoded in draco buffer."));
return false; return false;
} }
RWGltf_GltfBufferView aBuffViewDraco; RWGltf_GltfBufferView aBuffViewDraco;
aBuffViewDraco.Id = aBuffId++; aBuffViewDraco.Id = (int)aBuffInd;
aBuffViewDraco.ByteOffset = aBinFile->tellp(); 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()) if (!aBinFile->good())
{ {
Message::SendFail (TCollection_AsciiString("File '") + myBinFileNameFull + "' cannot be written"); Message::SendFail (TCollection_AsciiString("File '") + myBinFileNameFull + "' cannot be written");

View File

@ -125,6 +125,12 @@ public:
//! May reduce binary data size thanks to smaller triangle indexes. //! May reduce binary data size thanks to smaller triangle indexes.
void SetSplitIndices16 (bool theToSplit) { myToSplitIndices16 = theToSplit; } 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 //! Return Draco parameters
const RWGltf_DracoParameters& CompressionParameters() const { return myDracoParameters; } const RWGltf_DracoParameters& CompressionParameters() const { return myDracoParameters; }
@ -397,6 +403,7 @@ protected:
int64_t myBinDataLen64; //!< length of binary file int64_t myBinDataLen64; //!< length of binary file
std::vector<RWGltf_GltfBufferView> myBuffViewsDraco; //!< vector of buffers view with compression data std::vector<RWGltf_GltfBufferView> myBuffViewsDraco; //!< vector of buffers view with compression data
Standard_Boolean myToParallel; //!< flag to use multithreading; FALSE by default
RWGltf_DracoParameters myDracoParameters; //!< Draco parameters RWGltf_DracoParameters myDracoParameters; //!< Draco parameters
}; };

View File

@ -383,6 +383,7 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI,
RWMesh_CoordinateSystem aSystemCoordSys = RWMesh_CoordinateSystem_Zup; RWMesh_CoordinateSystem aSystemCoordSys = RWMesh_CoordinateSystem_Zup;
bool toForceUVExport = false, toEmbedTexturesInGlb = true; bool toForceUVExport = false, toEmbedTexturesInGlb = true;
bool toMergeFaces = false, toSplitIndices16 = false; bool toMergeFaces = false, toSplitIndices16 = false;
bool isParallel = false;
RWMesh_NameFormat aNodeNameFormat = RWMesh_NameFormat_InstanceOrProduct; RWMesh_NameFormat aNodeNameFormat = RWMesh_NameFormat_InstanceOrProduct;
RWMesh_NameFormat aMeshNameFormat = RWMesh_NameFormat_Product; RWMesh_NameFormat aMeshNameFormat = RWMesh_NameFormat_Product;
RWGltf_DracoParameters aDracoParameters; RWGltf_DracoParameters aDracoParameters;
@ -556,6 +557,10 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI,
{ {
aDracoParameters.UnifiedQuantization = Draw::ParseOnOffIterator(theNbArgs, theArgVec, anArgIter); aDracoParameters.UnifiedQuantization = Draw::ParseOnOffIterator(theNbArgs, theArgVec, anArgIter);
} }
else if (anArgCase == "-parallel")
{
isParallel = Draw::ParseOnOffIterator(theNbArgs, theArgVec, anArgIter);
}
else else
{ {
Message::SendFail() << "Syntax error at '" << theArgVec[anArgIter] << "'"; Message::SendFail() << "Syntax error at '" << theArgVec[anArgIter] << "'";
@ -587,6 +592,7 @@ static Standard_Integer WriteGltf (Draw_Interpretor& theDI,
aWriter.SetToEmbedTexturesInGlb (toEmbedTexturesInGlb); aWriter.SetToEmbedTexturesInGlb (toEmbedTexturesInGlb);
aWriter.SetMergeFaces (toMergeFaces); aWriter.SetMergeFaces (toMergeFaces);
aWriter.SetSplitIndices16 (toSplitIndices16); aWriter.SetSplitIndices16 (toSplitIndices16);
aWriter.SetParallel(isParallel);
aWriter.SetCompressionParameters(aDracoParameters); aWriter.SetCompressionParameters(aDracoParameters);
aWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit (aScaleFactorM); aWriter.ChangeCoordinateSystemConverter().SetInputLengthUnit (aScaleFactorM);
aWriter.ChangeCoordinateSystemConverter().SetInputCoordinateSystem (aSystemCoordSys); 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: [-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: [-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: [-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: Write XDE document into glTF file."
"\n\t\t: -trsfFormat preferred transformation format" "\n\t\t: -trsfFormat preferred transformation format"
"\n\t\t: -systemCoordSys system coordinate system; Zup when not specified" "\n\t\t: -systemCoordSys system coordinate system; Zup when not specified"
@ -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: -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\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 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); __FILE__, WriteGltf, g);
theCommands.Add ("writegltf", theCommands.Add ("writegltf",
"writegltf shape file", "writegltf shape file",

View File

@ -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"