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 <chrono>
#include <set>
#include <sstream>
#include <Corrade/Containers/Optional.h>
@ -187,18 +186,6 @@ using namespace Magnum;
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 */
template<class T> std::string calculateBounds(Containers::Array<T>&& attribute) {
/** @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))
Containers::Array<const char, Utility::Directory::MapDeleter> mapped;
if(args.isSet("map")) {
Duration d{importTime};
Trade::Implementation::Duration d{importTime};
if(!(mapped = Utility::Directory::mapRead(args.value("input"))) || !importer->openMemory(mapped)) {
Error() << "Cannot memory-map file" << args.value("input");
return 3;
@ -327,7 +314,7 @@ used.)")
} else
#endif
{
Duration d{importTime};
Trade::Implementation::Duration d{importTime};
if(!importer->openFile(args.value("input"))) {
Error() << "Cannot open file" << args.value("input");
return 3;
@ -472,7 +459,7 @@ used.)")
if(args.isSet("info") || args.isSet("info-animations")) for(UnsignedInt i = 0; i != importer->animationCount(); ++i) {
Containers::Optional<Trade::AnimationData> animation;
{
Duration d{importTime};
Trade::Implementation::Duration d{importTime};
if(!(animation = importer->animation(i))) {
error = true;
continue;
@ -492,7 +479,7 @@ used.)")
if(args.isSet("info") || args.isSet("info-skins")) for(UnsignedInt i = 0; i != importer->skin3DCount(); ++i) {
Containers::Optional<Trade::SkinData3D> skin;
{
Duration d{importTime};
Trade::Implementation::Duration d{importTime};
if(!(skin = importer->skin3D(i))) {
error = true;
continue;
@ -512,7 +499,7 @@ used.)")
if(args.isSet("info") || args.isSet("info-lights")) for(UnsignedInt i = 0; i != importer->lightCount(); ++i) {
Containers::Optional<Trade::LightData> light;
{
Duration d{importTime};
Trade::Implementation::Duration d{importTime};
if(!(light = importer->light(i))) {
error = true;
continue;
@ -538,7 +525,7 @@ used.)")
for(UnsignedInt i = 0; i != importer->materialCount(); ++i) {
Containers::Optional<Trade::MaterialData> material;
{
Duration d{importTime};
Trade::Implementation::Duration d{importTime};
if(!(material = importer->material(i))) {
error = true;
continue;
@ -576,7 +563,7 @@ used.)")
for(UnsignedInt j = 0; j != importer->meshLevelCount(i); ++j) {
Containers::Optional<Trade::MeshData> mesh;
{
Duration d{importTime};
Trade::Implementation::Duration d{importTime};
if(!(mesh = importer->mesh(i, j))) {
error = true;
continue;
@ -667,7 +654,7 @@ used.)")
for(UnsignedInt i = 0; i != importer->textureCount(); ++i) {
Containers::Optional<Trade::TextureData> texture;
{
Duration d{importTime};
Trade::Implementation::Duration d{importTime};
if(!(texture = importer->texture(i))) {
error = true;
continue;
@ -707,7 +694,7 @@ used.)")
bool compactImages = false;
Containers::Array<Trade::Implementation::ImageInfo> imageInfos;
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) {
@ -982,7 +969,7 @@ used.)")
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")))) {
Error{} << "Cannot import the mesh";
return 4;
@ -1015,7 +1002,7 @@ used.)")
if(args.isSet("remove-duplicates")) {
const UnsignedInt beforeVertexCount = mesh->vertexCount();
{
Duration d{conversionTime};
Trade::Implementation::Duration d{conversionTime};
mesh = MeshTools::removeDuplicates(*std::move(mesh));
}
if(args.isSet("verbose"))
@ -1027,7 +1014,7 @@ used.)")
if(!args.value("remove-duplicates-fuzzy").empty()) {
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"));
}
if(args.isSet("verbose"))
@ -1065,7 +1052,7 @@ used.)")
if(converterCount > 1 && args.isSet("verbose"))
Debug{} << "Saving output with" << converterName << Debug::nospace << "...";
Duration d{conversionTime};
Trade::Implementation::Duration d{conversionTime};
if(!converter->convertToFile(*mesh, args.value("output"))) {
Error{} << "Cannot save file" << args.value("output");
return 5;
@ -1085,7 +1072,7 @@ used.)")
return 6;
}
Duration d{conversionTime};
Trade::Implementation::Duration d{conversionTime};
if(!(mesh = converter->convert(*mesh))) {
Error{} << converterName << "cannot convert the mesh";
return 7;

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

@ -25,6 +25,7 @@
DEALINGS IN THE SOFTWARE.
*/
#include <chrono>
#include <Corrade/Containers/GrowableArray.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 */
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 {
UnsignedInt image, level;
bool compressed;
@ -44,7 +57,7 @@ struct ImageInfo {
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;
for(UnsignedInt i = 0; i != importer.image1DCount(); ++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;
for(UnsignedInt j = 0; j != levelCount; ++j) {
Containers::Optional<Trade::ImageData1D> image = importer.image1D(i, j);
if(!image) {
error = true;
continue;
Containers::Optional<Trade::ImageData1D> image;
{
Duration d{importTime};
if(!(image = importer.image1D(i, j))) {
error = true;
continue;
}
}
arrayAppend(infos, InPlaceInit, i, j,
image->isCompressed(),
@ -73,10 +89,13 @@ Containers::Array<ImageInfo> imageInfo(AbstractImporter& importer, bool& error,
if(!name.empty() || levelCount != 1) compact = false;
for(UnsignedInt j = 0; j != levelCount; ++j) {
Containers::Optional<Trade::ImageData2D> image = importer.image2D(i, j);
if(!image) {
error = true;
continue;
Containers::Optional<Trade::ImageData2D> image;
{
Duration d{importTime};
if(!(image = importer.image2D(i, j))) {
error = true;
continue;
}
}
arrayAppend(infos, InPlaceInit, i, j,
image->isCompressed(),
@ -94,10 +113,13 @@ Containers::Array<ImageInfo> imageInfo(AbstractImporter& importer, bool& error,
if(!name.empty() || levelCount != 1) compact = false;
for(UnsignedInt j = 0; j != levelCount; ++j) {
Containers::Optional<Trade::ImageData3D> image = importer.image3D(i, j);
if(!image) {
error = true;
continue;
Containers::Optional<Trade::ImageData3D> image;
{
Duration d{importTime};
if(!(image = importer.image3D(i, j))) {
error = true;
continue;
}
}
arrayAppend(infos, InPlaceInit, i, j,
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,]
[-c|--converter-options key=val,key2=val2,] [-D|--dimensions N]
[--image N] [--level N] [--layer N] [--layers] [--levels] [--in-place]
[--info] [-v|--verbose] [--] input output
[--info] [-v|--verbose] [--profile] [--] input output
@endcode
Arguments:
@ -102,6 +102,7 @@ Arguments:
- `--in-place` --- overwrite the input image with the output
- `--info` --- print info about the input file and exit
- `-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
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("info").setHelp("info", "print info about the input file and exit")
.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) {
/* If --in-place or --info is passed, we don't need the output
argument */
@ -367,6 +369,8 @@ key=true; configuration subgroups are delimited with /.)")
Containers::Array<Trade::ImageData2D> images2D;
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) {
const std::string input = args.arrayValue("input", i);
@ -393,7 +397,11 @@ key=true; configuration subgroups are delimited with /.)")
Error{} << "Cannot open file" << input;
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));
if(data.size() % pixelSize || side*side*pixelSize != data.size()) {
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 */
if(args.isSet("info")) {
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;
}
@ -421,9 +434,12 @@ key=true; configuration subgroups are delimited with /.)")
Implementation::setOptions(*importer, "AnyImageImporter", args.value("importer-options"));
/* Open input file */
if(!importer->openFile(input)) {
Error{} << "Cannot open file" << input;
return 3;
{
Trade::Implementation::Duration d{importTime};
if(!importer->openFile(input)) {
Error{} << "Cannot open file" << input;
return 3;
}
}
/* Print image info, if requested. This is always done for just one
@ -442,7 +458,7 @@ key=true; configuration subgroups are delimited with /.)")
levels. */
bool error = false, compact = true;
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) {
Debug d;
@ -462,6 +478,10 @@ key=true; configuration subgroups are delimited with /.)")
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;
}
@ -604,6 +624,8 @@ key=true; configuration subgroups are delimited with /.)")
}
}
std::chrono::high_resolution_clock::duration conversionTime;
std::string output;
if(args.isSet("in-place")) {
/* 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 */
if(args.isSet("layers")) {
/* To include allocation + copy costs in the output */
Trade::Implementation::Duration d{conversionTime};
if(dimensions == 1) {
if(!checkCommonFormatAndSize(args, images1D)) return 1;
@ -814,7 +839,18 @@ key=true; configuration subgroups are delimited with /.)")
CORRADE_INTERNAL_ASSERT(outputImages3D.size() == 1);
data = outputImages3D.front().data();
} 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 */
@ -833,15 +869,23 @@ key=true; configuration subgroups are delimited with /.)")
/* Save output file */
bool converted;
if(outputDimensions == 1)
converted = convertOneOrMoreImages(*converter, outputImages1D, output);
else if(outputDimensions == 2)
converted = convertOneOrMoreImages(*converter, outputImages2D, output);
else if(outputDimensions == 3)
converted = convertOneOrMoreImages(*converter, outputImages3D, output);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
{
Trade::Implementation::Duration d{conversionTime};
if(outputDimensions == 1)
converted = convertOneOrMoreImages(*converter, outputImages1D, output);
else if(outputDimensions == 2)
converted = convertOneOrMoreImages(*converter, outputImages2D, output);
else if(outputDimensions == 3)
converted = convertOneOrMoreImages(*converter, outputImages3D, output);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
}
if(!converted) {
Error{} << "Cannot save file" << output;
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