Browse Source

imageconverter: add a --profile option.

Similar to sceneconverter's --profile option, measuring import and
conversion time. This also means that sceneconverter's --profile now
includes image import time, which wasn't done before.
pull/542/merge
Vladimír Vondruš 4 years ago
parent
commit
2303c7a341
  1. 41
      src/Magnum/MeshTools/sceneconverter.cpp
  2. 48
      src/Magnum/Trade/Implementation/converterUtilities.h
  3. 72
      src/Magnum/Trade/imageconverter.cpp

41
src/Magnum/MeshTools/sceneconverter.cpp

@ -24,7 +24,6 @@
*/ */
#include <algorithm> #include <algorithm>
#include <chrono>
#include <set> #include <set>
#include <sstream> #include <sstream>
#include <Corrade/Containers/Optional.h> #include <Corrade/Containers/Optional.h>
@ -187,18 +186,6 @@ using namespace Magnum;
namespace { namespace {
struct Duration {
explicit Duration(std::chrono::high_resolution_clock::duration& output): _output(output), _t{std::chrono::high_resolution_clock::now()} {}
~Duration() {
_output += std::chrono::high_resolution_clock::now() - _t;
}
private:
std::chrono::high_resolution_clock::duration& _output;
std::chrono::high_resolution_clock::time_point _t;
};
/** @todo const Array& doesn't work, minmax() would fail to match */ /** @todo const Array& doesn't work, minmax() would fail to match */
template<class T> std::string calculateBounds(Containers::Array<T>&& attribute) { template<class T> std::string calculateBounds(Containers::Array<T>&& attribute) {
/** @todo clean up when Debug::toString() exists */ /** @todo clean up when Debug::toString() exists */
@ -319,7 +306,7 @@ used.)")
#if defined(CORRADE_TARGET_UNIX) || (defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)) #if defined(CORRADE_TARGET_UNIX) || (defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT))
Containers::Array<const char, Utility::Directory::MapDeleter> mapped; Containers::Array<const char, Utility::Directory::MapDeleter> mapped;
if(args.isSet("map")) { if(args.isSet("map")) {
Duration d{importTime}; Trade::Implementation::Duration d{importTime};
if(!(mapped = Utility::Directory::mapRead(args.value("input"))) || !importer->openMemory(mapped)) { if(!(mapped = Utility::Directory::mapRead(args.value("input"))) || !importer->openMemory(mapped)) {
Error() << "Cannot memory-map file" << args.value("input"); Error() << "Cannot memory-map file" << args.value("input");
return 3; return 3;
@ -327,7 +314,7 @@ used.)")
} else } else
#endif #endif
{ {
Duration d{importTime}; Trade::Implementation::Duration d{importTime};
if(!importer->openFile(args.value("input"))) { if(!importer->openFile(args.value("input"))) {
Error() << "Cannot open file" << args.value("input"); Error() << "Cannot open file" << args.value("input");
return 3; return 3;
@ -472,7 +459,7 @@ used.)")
if(args.isSet("info") || args.isSet("info-animations")) for(UnsignedInt i = 0; i != importer->animationCount(); ++i) { if(args.isSet("info") || args.isSet("info-animations")) for(UnsignedInt i = 0; i != importer->animationCount(); ++i) {
Containers::Optional<Trade::AnimationData> animation; Containers::Optional<Trade::AnimationData> animation;
{ {
Duration d{importTime}; Trade::Implementation::Duration d{importTime};
if(!(animation = importer->animation(i))) { if(!(animation = importer->animation(i))) {
error = true; error = true;
continue; continue;
@ -492,7 +479,7 @@ used.)")
if(args.isSet("info") || args.isSet("info-skins")) for(UnsignedInt i = 0; i != importer->skin3DCount(); ++i) { if(args.isSet("info") || args.isSet("info-skins")) for(UnsignedInt i = 0; i != importer->skin3DCount(); ++i) {
Containers::Optional<Trade::SkinData3D> skin; Containers::Optional<Trade::SkinData3D> skin;
{ {
Duration d{importTime}; Trade::Implementation::Duration d{importTime};
if(!(skin = importer->skin3D(i))) { if(!(skin = importer->skin3D(i))) {
error = true; error = true;
continue; continue;
@ -512,7 +499,7 @@ used.)")
if(args.isSet("info") || args.isSet("info-lights")) for(UnsignedInt i = 0; i != importer->lightCount(); ++i) { if(args.isSet("info") || args.isSet("info-lights")) for(UnsignedInt i = 0; i != importer->lightCount(); ++i) {
Containers::Optional<Trade::LightData> light; Containers::Optional<Trade::LightData> light;
{ {
Duration d{importTime}; Trade::Implementation::Duration d{importTime};
if(!(light = importer->light(i))) { if(!(light = importer->light(i))) {
error = true; error = true;
continue; continue;
@ -538,7 +525,7 @@ used.)")
for(UnsignedInt i = 0; i != importer->materialCount(); ++i) { for(UnsignedInt i = 0; i != importer->materialCount(); ++i) {
Containers::Optional<Trade::MaterialData> material; Containers::Optional<Trade::MaterialData> material;
{ {
Duration d{importTime}; Trade::Implementation::Duration d{importTime};
if(!(material = importer->material(i))) { if(!(material = importer->material(i))) {
error = true; error = true;
continue; continue;
@ -576,7 +563,7 @@ used.)")
for(UnsignedInt j = 0; j != importer->meshLevelCount(i); ++j) { for(UnsignedInt j = 0; j != importer->meshLevelCount(i); ++j) {
Containers::Optional<Trade::MeshData> mesh; Containers::Optional<Trade::MeshData> mesh;
{ {
Duration d{importTime}; Trade::Implementation::Duration d{importTime};
if(!(mesh = importer->mesh(i, j))) { if(!(mesh = importer->mesh(i, j))) {
error = true; error = true;
continue; continue;
@ -667,7 +654,7 @@ used.)")
for(UnsignedInt i = 0; i != importer->textureCount(); ++i) { for(UnsignedInt i = 0; i != importer->textureCount(); ++i) {
Containers::Optional<Trade::TextureData> texture; Containers::Optional<Trade::TextureData> texture;
{ {
Duration d{importTime}; Trade::Implementation::Duration d{importTime};
if(!(texture = importer->texture(i))) { if(!(texture = importer->texture(i))) {
error = true; error = true;
continue; continue;
@ -707,7 +694,7 @@ used.)")
bool compactImages = false; bool compactImages = false;
Containers::Array<Trade::Implementation::ImageInfo> imageInfos; Containers::Array<Trade::Implementation::ImageInfo> imageInfos;
if(args.isSet("info") || args.isSet("info-images")) { if(args.isSet("info") || args.isSet("info-images")) {
imageInfos = Trade::Implementation::imageInfo(*importer, error, compactImages); imageInfos = Trade::Implementation::imageInfo(*importer, error, compactImages, importTime);
} }
for(const SceneInfo& info: sceneInfos) { for(const SceneInfo& info: sceneInfos) {
@ -982,7 +969,7 @@ used.)")
Containers::Optional<Trade::MeshData> mesh; Containers::Optional<Trade::MeshData> mesh;
{ {
Duration d{importTime}; Trade::Implementation::Duration d{importTime};
if(!importer->meshCount() || !(mesh = importer->mesh(args.value<UnsignedInt>("mesh"), args.value<UnsignedInt>("level")))) { if(!importer->meshCount() || !(mesh = importer->mesh(args.value<UnsignedInt>("mesh"), args.value<UnsignedInt>("level")))) {
Error{} << "Cannot import the mesh"; Error{} << "Cannot import the mesh";
return 4; return 4;
@ -1015,7 +1002,7 @@ used.)")
if(args.isSet("remove-duplicates")) { if(args.isSet("remove-duplicates")) {
const UnsignedInt beforeVertexCount = mesh->vertexCount(); const UnsignedInt beforeVertexCount = mesh->vertexCount();
{ {
Duration d{conversionTime}; Trade::Implementation::Duration d{conversionTime};
mesh = MeshTools::removeDuplicates(*std::move(mesh)); mesh = MeshTools::removeDuplicates(*std::move(mesh));
} }
if(args.isSet("verbose")) if(args.isSet("verbose"))
@ -1027,7 +1014,7 @@ used.)")
if(!args.value("remove-duplicates-fuzzy").empty()) { if(!args.value("remove-duplicates-fuzzy").empty()) {
const UnsignedInt beforeVertexCount = mesh->vertexCount(); const UnsignedInt beforeVertexCount = mesh->vertexCount();
{ {
Duration d{conversionTime}; Trade::Implementation::Duration d{conversionTime};
mesh = MeshTools::removeDuplicatesFuzzy(*std::move(mesh), args.value<Float>("remove-duplicates-fuzzy")); mesh = MeshTools::removeDuplicatesFuzzy(*std::move(mesh), args.value<Float>("remove-duplicates-fuzzy"));
} }
if(args.isSet("verbose")) if(args.isSet("verbose"))
@ -1065,7 +1052,7 @@ used.)")
if(converterCount > 1 && args.isSet("verbose")) if(converterCount > 1 && args.isSet("verbose"))
Debug{} << "Saving output with" << converterName << Debug::nospace << "..."; Debug{} << "Saving output with" << converterName << Debug::nospace << "...";
Duration d{conversionTime}; Trade::Implementation::Duration d{conversionTime};
if(!converter->convertToFile(*mesh, args.value("output"))) { if(!converter->convertToFile(*mesh, args.value("output"))) {
Error{} << "Cannot save file" << args.value("output"); Error{} << "Cannot save file" << args.value("output");
return 5; return 5;
@ -1085,7 +1072,7 @@ used.)")
return 6; return 6;
} }
Duration d{conversionTime}; Trade::Implementation::Duration d{conversionTime};
if(!(mesh = converter->convert(*mesh))) { if(!(mesh = converter->convert(*mesh))) {
Error{} << converterName << "cannot convert the mesh"; Error{} << converterName << "cannot convert the mesh";
return 7; return 7;

48
src/Magnum/Trade/Implementation/converterUtilities.h

@ -25,6 +25,7 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include <chrono>
#include <Corrade/Containers/GrowableArray.h> #include <Corrade/Containers/GrowableArray.h>
#include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/AbstractImporter.h"
@ -35,6 +36,18 @@ namespace Magnum { namespace Trade { namespace Implementation {
/* Used only in executables where we don't want it to be exported */ /* Used only in executables where we don't want it to be exported */
namespace { namespace {
struct Duration {
explicit Duration(std::chrono::high_resolution_clock::duration& output): _output(output), _t{std::chrono::high_resolution_clock::now()} {}
~Duration() {
_output += std::chrono::high_resolution_clock::now() - _t;
}
private:
std::chrono::high_resolution_clock::duration& _output;
std::chrono::high_resolution_clock::time_point _t;
};
struct ImageInfo { struct ImageInfo {
UnsignedInt image, level; UnsignedInt image, level;
bool compressed; bool compressed;
@ -44,7 +57,7 @@ struct ImageInfo {
std::string name; std::string name;
}; };
Containers::Array<ImageInfo> imageInfo(AbstractImporter& importer, bool& error, bool& compact) { Containers::Array<ImageInfo> imageInfo(AbstractImporter& importer, bool& error, bool& compact, std::chrono::high_resolution_clock::duration& importTime) {
Containers::Array<ImageInfo> infos; Containers::Array<ImageInfo> infos;
for(UnsignedInt i = 0; i != importer.image1DCount(); ++i) { for(UnsignedInt i = 0; i != importer.image1DCount(); ++i) {
const std::string name = importer.image1DName(i); const std::string name = importer.image1DName(i);
@ -52,10 +65,13 @@ Containers::Array<ImageInfo> imageInfo(AbstractImporter& importer, bool& error,
if(!name.empty() || levelCount != 1) compact = false; if(!name.empty() || levelCount != 1) compact = false;
for(UnsignedInt j = 0; j != levelCount; ++j) { for(UnsignedInt j = 0; j != levelCount; ++j) {
Containers::Optional<Trade::ImageData1D> image = importer.image1D(i, j); Containers::Optional<Trade::ImageData1D> image;
if(!image) { {
error = true; Duration d{importTime};
continue; if(!(image = importer.image1D(i, j))) {
error = true;
continue;
}
} }
arrayAppend(infos, InPlaceInit, i, j, arrayAppend(infos, InPlaceInit, i, j,
image->isCompressed(), image->isCompressed(),
@ -73,10 +89,13 @@ Containers::Array<ImageInfo> imageInfo(AbstractImporter& importer, bool& error,
if(!name.empty() || levelCount != 1) compact = false; if(!name.empty() || levelCount != 1) compact = false;
for(UnsignedInt j = 0; j != levelCount; ++j) { for(UnsignedInt j = 0; j != levelCount; ++j) {
Containers::Optional<Trade::ImageData2D> image = importer.image2D(i, j); Containers::Optional<Trade::ImageData2D> image;
if(!image) { {
error = true; Duration d{importTime};
continue; if(!(image = importer.image2D(i, j))) {
error = true;
continue;
}
} }
arrayAppend(infos, InPlaceInit, i, j, arrayAppend(infos, InPlaceInit, i, j,
image->isCompressed(), image->isCompressed(),
@ -94,10 +113,13 @@ Containers::Array<ImageInfo> imageInfo(AbstractImporter& importer, bool& error,
if(!name.empty() || levelCount != 1) compact = false; if(!name.empty() || levelCount != 1) compact = false;
for(UnsignedInt j = 0; j != levelCount; ++j) { for(UnsignedInt j = 0; j != levelCount; ++j) {
Containers::Optional<Trade::ImageData3D> image = importer.image3D(i, j); Containers::Optional<Trade::ImageData3D> image;
if(!image) { {
error = true; Duration d{importTime};
continue; if(!(image = importer.image3D(i, j))) {
error = true;
continue;
}
} }
arrayAppend(infos, InPlaceInit, i, j, arrayAppend(infos, InPlaceInit, i, j,
image->isCompressed(), image->isCompressed(),

72
src/Magnum/Trade/imageconverter.cpp

@ -73,7 +73,7 @@ magnum-imageconverter [-h|--help] [-I|--importer PLUGIN]
[-i|--importer-options key=val,key2=val2,] [-i|--importer-options key=val,key2=val2,]
[-c|--converter-options key=val,key2=val2,] [-D|--dimensions N] [-c|--converter-options key=val,key2=val2,] [-D|--dimensions N]
[--image N] [--level N] [--layer N] [--layers] [--levels] [--in-place] [--image N] [--level N] [--layer N] [--layers] [--levels] [--in-place]
[--info] [-v|--verbose] [--] input output [--info] [-v|--verbose] [--profile] [--] input output
@endcode @endcode
Arguments: Arguments:
@ -102,6 +102,7 @@ Arguments:
- `--in-place` --- overwrite the input image with the output - `--in-place` --- overwrite the input image with the output
- `--info` --- print info about the input file and exit - `--info` --- print info about the input file and exit
- `-v`, `--verbose` --- verbose output from importer and converter plugins - `-v`, `--verbose` --- verbose output from importer and converter plugins
- `--profile` --- measure import and conversion time
Specifying `--importer raw:&lt;format&gt;` will treat the input as a raw Specifying `--importer raw:&lt;format&gt;` will treat the input as a raw
tightly-packed square of pixels in given @ref PixelFormat. Specifying `-C` / tightly-packed square of pixels in given @ref PixelFormat. Specifying `-C` /
@ -282,6 +283,7 @@ int main(int argc, char** argv) {
.addBooleanOption("in-place").setHelp("in-place", "overwrite the input image with the output") .addBooleanOption("in-place").setHelp("in-place", "overwrite the input image with the output")
.addBooleanOption("info").setHelp("info", "print info about the input file and exit") .addBooleanOption("info").setHelp("info", "print info about the input file and exit")
.addBooleanOption('v', "verbose").setHelp("verbose", "verbose output from importer and converter plugins") .addBooleanOption('v', "verbose").setHelp("verbose", "verbose output from importer and converter plugins")
.addBooleanOption("profile").setHelp("profile", "measure import and conversion time")
.setParseErrorCallback([](const Utility::Arguments& args, Utility::Arguments::ParseError error, const std::string& key) { .setParseErrorCallback([](const Utility::Arguments& args, Utility::Arguments::ParseError error, const std::string& key) {
/* If --in-place or --info is passed, we don't need the output /* If --in-place or --info is passed, we don't need the output
argument */ argument */
@ -367,6 +369,8 @@ key=true; configuration subgroups are delimited with /.)")
Containers::Array<Trade::ImageData2D> images2D; Containers::Array<Trade::ImageData2D> images2D;
Containers::Array<Trade::ImageData3D> images3D; Containers::Array<Trade::ImageData3D> images3D;
std::chrono::high_resolution_clock::duration importTime;
for(std::size_t i = 0, max = args.arrayValueCount("input"); i != max; ++i) { for(std::size_t i = 0, max = args.arrayValueCount("input"); i != max; ++i) {
const std::string input = args.arrayValue("input", i); const std::string input = args.arrayValue("input", i);
@ -393,7 +397,11 @@ key=true; configuration subgroups are delimited with /.)")
Error{} << "Cannot open file" << input; Error{} << "Cannot open file" << input;
return 3; return 3;
} }
Containers::Array<char> data = Utility::Directory::read(input); Containers::Array<char> data;
{
Trade::Implementation::Duration d{importTime};
data = Utility::Directory::read(input);
}
auto side = Int(std::sqrt(data.size()/pixelSize)); auto side = Int(std::sqrt(data.size()/pixelSize));
if(data.size() % pixelSize || side*side*pixelSize != data.size()) { if(data.size() % pixelSize || side*side*pixelSize != data.size()) {
Error{} << "File of size" << data.size() << "is not a tightly-packed square of" << format; Error{} << "File of size" << data.size() << "is not a tightly-packed square of" << format;
@ -403,6 +411,11 @@ key=true; configuration subgroups are delimited with /.)")
/* Print image info, if requested */ /* Print image info, if requested */
if(args.isSet("info")) { if(args.isSet("info")) {
Debug{} << "Image 0:\n Mip 0:" << format << Vector2i{side}; Debug{} << "Image 0:\n Mip 0:" << format << Vector2i{side};
if(args.isSet("profile")) {
Debug{} << "Import took" << UnsignedInt(std::chrono::duration_cast<std::chrono::milliseconds>(importTime).count())/1.0e3f << "seconds";
}
return 0; return 0;
} }
@ -421,9 +434,12 @@ key=true; configuration subgroups are delimited with /.)")
Implementation::setOptions(*importer, "AnyImageImporter", args.value("importer-options")); Implementation::setOptions(*importer, "AnyImageImporter", args.value("importer-options"));
/* Open input file */ /* Open input file */
if(!importer->openFile(input)) { {
Error{} << "Cannot open file" << input; Trade::Implementation::Duration d{importTime};
return 3; if(!importer->openFile(input)) {
Error{} << "Cannot open file" << input;
return 3;
}
} }
/* Print image info, if requested. This is always done for just one /* Print image info, if requested. This is always done for just one
@ -442,7 +458,7 @@ key=true; configuration subgroups are delimited with /.)")
levels. */ levels. */
bool error = false, compact = true; bool error = false, compact = true;
Containers::Array<Trade::Implementation::ImageInfo> infos = Containers::Array<Trade::Implementation::ImageInfo> infos =
Trade::Implementation::imageInfo(*importer, error, compact); Trade::Implementation::imageInfo(*importer, error, compact, importTime);
for(const Trade::Implementation::ImageInfo& info: infos) { for(const Trade::Implementation::ImageInfo& info: infos) {
Debug d; Debug d;
@ -462,6 +478,10 @@ key=true; configuration subgroups are delimited with /.)")
else d << Math::Vector<1, Int>(info.size.x()); else d << Math::Vector<1, Int>(info.size.x());
} }
if(args.isSet("profile")) {
Debug{} << "Import took" << UnsignedInt(std::chrono::duration_cast<std::chrono::milliseconds>(importTime).count())/1.0e3f << "seconds";
}
return error ? 1 : 0; return error ? 1 : 0;
} }
@ -604,6 +624,8 @@ key=true; configuration subgroups are delimited with /.)")
} }
} }
std::chrono::high_resolution_clock::duration conversionTime;
std::string output; std::string output;
if(args.isSet("in-place")) { if(args.isSet("in-place")) {
/* Should have been checked in a graceful way above */ /* Should have been checked in a graceful way above */
@ -618,6 +640,9 @@ key=true; configuration subgroups are delimited with /.)")
/* Combine multiple layers into an image of one dimension more */ /* Combine multiple layers into an image of one dimension more */
if(args.isSet("layers")) { if(args.isSet("layers")) {
/* To include allocation + copy costs in the output */
Trade::Implementation::Duration d{conversionTime};
if(dimensions == 1) { if(dimensions == 1) {
if(!checkCommonFormatAndSize(args, images1D)) return 1; if(!checkCommonFormatAndSize(args, images1D)) return 1;
@ -814,7 +839,18 @@ key=true; configuration subgroups are delimited with /.)")
CORRADE_INTERNAL_ASSERT(outputImages3D.size() == 1); CORRADE_INTERNAL_ASSERT(outputImages3D.size() == 1);
data = outputImages3D.front().data(); data = outputImages3D.front().data();
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
return Utility::Directory::write(output, data) ? 0 : 1;
{
Trade::Implementation::Duration d{conversionTime};
if(!Utility::Directory::write(output, data)) return 1;
}
if(args.isSet("profile")) {
Debug{} << "Import took" << UnsignedInt(std::chrono::duration_cast<std::chrono::milliseconds>(importTime).count())/1.0e3f << "seconds, conversion"
<< UnsignedInt(std::chrono::duration_cast<std::chrono::milliseconds>(conversionTime).count())/1.0e3f << "seconds";
}
return 0;
} }
/* Load converter plugin */ /* Load converter plugin */
@ -833,15 +869,23 @@ key=true; configuration subgroups are delimited with /.)")
/* Save output file */ /* Save output file */
bool converted; bool converted;
if(outputDimensions == 1) {
converted = convertOneOrMoreImages(*converter, outputImages1D, output); Trade::Implementation::Duration d{conversionTime};
else if(outputDimensions == 2) if(outputDimensions == 1)
converted = convertOneOrMoreImages(*converter, outputImages2D, output); converted = convertOneOrMoreImages(*converter, outputImages1D, output);
else if(outputDimensions == 3) else if(outputDimensions == 2)
converted = convertOneOrMoreImages(*converter, outputImages3D, output); converted = convertOneOrMoreImages(*converter, outputImages2D, output);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); else if(outputDimensions == 3)
converted = convertOneOrMoreImages(*converter, outputImages3D, output);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
}
if(!converted) { if(!converted) {
Error{} << "Cannot save file" << output; Error{} << "Cannot save file" << output;
return 5; return 5;
} }
if(args.isSet("profile")) {
Debug{} << "Import took" << UnsignedInt(std::chrono::duration_cast<std::chrono::milliseconds>(importTime).count())/1.0e3f << "seconds, conversion"
<< UnsignedInt(std::chrono::duration_cast<std::chrono::milliseconds>(conversionTime).count())/1.0e3f << "seconds";
}
} }

Loading…
Cancel
Save