Skip to content

Commit

Permalink
perf(oiiotool): use pointer not static for internal color config (#4433)
Browse files Browse the repository at this point in the history
Basically, this switches to creating the colorconfig that oiiotool uses
from statically constructed to build-on-demand, which should slightly
lower runtime overhead for oiiotool invocations that don't need to do
any color transformations.

Signed-off-by: Larry Gritz <[email protected]>
  • Loading branch information
lgritz committed Sep 22, 2024
1 parent d950d03 commit fe9f7f8
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 41 deletions.
102 changes: 62 additions & 40 deletions src/oiiotool/oiiotool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,27 @@ Oiiotool::clear_options()



ColorConfig&
Oiiotool::colorconfig()
{
// It's safe to check the pointer and if it exists, return it, since
// once it's created, it will never be changed.
if (ColorConfig* cc = m_colorconfig.get())
return *cc;

// Otherwise, we need to create it. But we need to be thread-safe.
static std::mutex colorconfig_mutex;
std::lock_guard lock(colorconfig_mutex);
if (!m_colorconfig) {
if (debug)
print("oiiotool Creating ColorConfig\n");
m_colorconfig.reset(new ColorConfig);
}
return *m_colorconfig.get();
}



void
Oiiotool::clear_input_config()
{
Expand Down Expand Up @@ -2192,9 +2213,9 @@ static void
set_colorconfig(Oiiotool& ot, cspan<const char*> argv)
{
OIIO_DASSERT(argv.size() == 2);
ot.colorconfig.reset(argv[1]);
if (ot.colorconfig.has_error()) {
ot.errorfmt("--colorconfig", "{}", ot.colorconfig.geterror());
ot.colorconfig().reset(argv[1]);
if (ot.colorconfig().has_error()) {
ot.errorfmt("--colorconfig", "{}", ot.colorconfig().geterror());
}
}

Expand Down Expand Up @@ -2279,7 +2300,7 @@ class OpColorConvert final : public OiiotoolOp {
}
bool ok = ImageBufAlgo::colorconvert(*img[0], *img[1], fromspace,
tospace, unpremult, contextkey,
contextvalue, &ot.colorconfig);
contextvalue, &ot.colorconfig());
if (!ok && !strict) {
// The color transform failed, but we were told not to be
// strict, so ignore the error and just copy destination to
Expand Down Expand Up @@ -2354,7 +2375,7 @@ OIIOTOOL_OP(ociolook, 1, [&](OiiotoolOp& op, span<ImageBuf*> img) {
tospace = img[1]->spec().get_string_attribute("oiio:Colorspace");
return ImageBufAlgo::ociolook(*img[0], *img[1], lookname, fromspace,
tospace, unpremult, inverse, contextkey,
contextvalue, &ot.colorconfig);
contextvalue, &ot.colorconfig());
});


Expand All @@ -2373,7 +2394,8 @@ OIIOTOOL_OP(ociodisplay, 1, [&](OiiotoolOp& op, span<ImageBuf*> img) {
fromspace = img[1]->spec().get_string_attribute("oiio:Colorspace");
return ImageBufAlgo::ociodisplay(*img[0], *img[1], displayname, viewname,
fromspace, looks, unpremult, inverse,
contextkey, contextvalue, &ot.colorconfig);
contextkey, contextvalue,
&ot.colorconfig());
});


Expand All @@ -2384,7 +2406,7 @@ OIIOTOOL_OP(ociofiletransform, 1, [&](OiiotoolOp& op, span<ImageBuf*> img) {
bool inverse = op.options().get_int("inverse");
bool unpremult = op.options().get_int("unpremult");
return ImageBufAlgo::ociofiletransform(*img[0], *img[1], name, unpremult,
inverse, &ot.colorconfig);
inverse, &ot.colorconfig());
});


Expand All @@ -2398,7 +2420,7 @@ OIIOTOOL_OP(ocionamedtransform, 1, [&](OiiotoolOp& op, span<ImageBuf*> img) {
bool inverse = op.options().get_int("inverse");
return ImageBufAlgo::ocionamedtransform(*img[0], *img[1], name, unpremult,
inverse, contextkey, contextvalue,
&ot.colorconfig);
&ot.colorconfig());
});


Expand Down Expand Up @@ -5161,7 +5183,7 @@ input_file(Oiiotool& ot, cspan<const char*> argv)
if (autocc) {
// Try to deduce the color space it's in
std::string colorspace(
ot.colorconfig.getColorSpaceFromFilepath(filename));
ot.colorconfig().getColorSpaceFromFilepath(filename));
if (colorspace.size() && ot.debug)
print(" From {}, we deduce color space \"{}\"\n", filename,
colorspace);
Expand All @@ -5173,9 +5195,9 @@ input_file(Oiiotool& ot, cspan<const char*> argv)
print(" Metadata of {} indicates color space \"{}\"\n",
colorspace, filename);
}
std::string linearspace = ot.colorconfig.resolve("linear");
std::string linearspace = ot.colorconfig().resolve("linear");
if (colorspace.size()
&& !ot.colorconfig.equivalent(colorspace, linearspace)) {
&& !ot.colorconfig().equivalent(colorspace, linearspace)) {
std::string cmd = "colorconvert:strict=0";
if (autoccunpremult)
cmd += ":unpremult=1";
Expand Down Expand Up @@ -5459,12 +5481,12 @@ output_file(Oiiotool& ot, cspan<const char*> argv)
// automatically set -d based on the name if --autocc is used.
bool autocc = fileoptions.get_int("autocc", ot.autocc);
bool autoccunpremult = fileoptions.get_int("unpremult", ot.autoccunpremult);
std::string outcolorspace = ot.colorconfig.getColorSpaceFromFilepath(
std::string outcolorspace = ot.colorconfig().getColorSpaceFromFilepath(
filename);
if (autocc && outcolorspace.size()) {
TypeDesc type;
int bits;
type = ot.colorconfig.getColorSpaceDataType(outcolorspace, &bits);
type = ot.colorconfig().getColorSpaceDataType(outcolorspace, &bits);
if (type.basetype != TypeDesc::UNKNOWN) {
if (ot.debug)
std::cout << " Deduced data type " << type << " (" << bits
Expand All @@ -5482,7 +5504,7 @@ output_file(Oiiotool& ot, cspan<const char*> argv)
}
}
if (autocc) {
string_view linearspace = ot.colorconfig.resolve("linear");
string_view linearspace = ot.colorconfig().resolve("linear");
std::string currentspace
= ir->spec()->get_string_attribute("oiio:ColorSpace", linearspace);
// Special cases where we know formats should be particular color
Expand Down Expand Up @@ -5978,66 +6000,66 @@ print_ocio_info(Oiiotool& ot, std::ostream& out)
using Strutil::print;
int columns = Sysutil::terminal_columns() - 1;

int ociover = ot.colorconfig.OpenColorIO_version_hex();
if (ociover)
ColorConfig& colorconfig = ot.colorconfig();
if (int ociover = colorconfig.OpenColorIO_version_hex())
out << "OpenColorIO " << (ociover >> 24) << '.'
<< ((ociover >> 16) & 0xff) << '.' << ((ociover >> 8) & 0xff);
else
out << "No OpenColorIO";
out << "\nColor config: " << ot.colorconfig.configname() << "\n";
out << "\nColor config: " << colorconfig.configname() << "\n";
out << "Known color spaces: \n";
const char* linear = ot.colorconfig.getColorSpaceNameByRole("linear");
for (int i = 0, e = ot.colorconfig.getNumColorSpaces(); i < e; ++i) {
const char* n = ot.colorconfig.getColorSpaceNameByIndex(i);
const char* linear = colorconfig.getColorSpaceNameByRole("linear");
for (int i = 0, e = colorconfig.getNumColorSpaces(); i < e; ++i) {
const char* n = colorconfig.getColorSpaceNameByIndex(i);
out << " - " << quote_if_spaces(n);
if ((linear && !ot.colorconfig.equivalent(n, "linear")
&& ot.colorconfig.equivalent(n, linear))
|| ot.colorconfig.isColorSpaceLinear(n))
if ((linear && !colorconfig.equivalent(n, "linear")
&& colorconfig.equivalent(n, linear))
|| colorconfig.isColorSpaceLinear(n))
out << " (linear)";
out << "\n";
auto aliases = ot.colorconfig.getAliases(n);
auto aliases = colorconfig.getAliases(n);
if (aliases.size()) {
std::stringstream s;
s << " aliases: " << join_with_quotes(aliases, ", ");
out << Strutil::wordwrap(s.str(), columns, 6) << "\n";
}
}

int roles = ot.colorconfig.getNumRoles();
int roles = colorconfig.getNumRoles();
if (roles) {
print(out, "Known roles:\n");
for (int i = 0; i < roles; ++i) {
const char* r = ot.colorconfig.getRoleByIndex(i);
const char* r = colorconfig.getRoleByIndex(i);
print(out, " - {} -> {}\n", quote_if_spaces(r),
quote_if_spaces(ot.colorconfig.getColorSpaceNameByRole(r)));
quote_if_spaces(colorconfig.getColorSpaceNameByRole(r)));
}
}

int nlooks = ot.colorconfig.getNumLooks();
int nlooks = colorconfig.getNumLooks();
if (nlooks) {
print(out, "Known looks:\n");
for (int i = 0; i < nlooks; ++i)
print(out, " - {}\n",
quote_if_spaces(ot.colorconfig.getLookNameByIndex(i)));
quote_if_spaces(colorconfig.getLookNameByIndex(i)));
}

const char* default_display = ot.colorconfig.getDefaultDisplayName();
int ndisplays = ot.colorconfig.getNumDisplays();
const char* default_display = colorconfig.getDefaultDisplayName();
int ndisplays = colorconfig.getNumDisplays();
if (ndisplays) {
out << "Known displays: (* indicates default)\n";
for (int i = 0; i < ndisplays; ++i) {
const char* d = ot.colorconfig.getDisplayNameByIndex(i);
const char* d = colorconfig.getDisplayNameByIndex(i);
out << " - " << quote_if_spaces(d);
if (!strcmp(d, default_display))
out << " (*)";
const char* default_view = ot.colorconfig.getDefaultViewName(d);
int nviews = ot.colorconfig.getNumViews(d);
const char* default_view = colorconfig.getDefaultViewName(d);
int nviews = colorconfig.getNumViews(d);
if (nviews) {
out << "\n ";
std::stringstream s;
s << "views: ";
for (int i = 0; i < nviews; ++i) {
const char* v = ot.colorconfig.getViewNameByIndex(d, i);
const char* v = colorconfig.getViewNameByIndex(d, i);
s << quote_if_spaces(v);
if (!strcmp(v, default_view))
s << " (*)";
Expand All @@ -6050,15 +6072,15 @@ print_ocio_info(Oiiotool& ot, std::ostream& out)
}
}

int nnamed_transforms = ot.colorconfig.getNumNamedTransforms();
int nnamed_transforms = colorconfig.getNumNamedTransforms();
if (nnamed_transforms) {
out << "Named transforms:\n";
for (int i = 0; i < nnamed_transforms; ++i) {
const char* x = ot.colorconfig.getNamedTransformNameByIndex(i);
const char* x = colorconfig.getNamedTransformNameByIndex(i);
out << " - " << quote_if_spaces(x) << "\n";
}
}
if (!ot.colorconfig.supportsOpenColorIO())
if (!colorconfig.supportsOpenColorIO())
out << "No OpenColorIO support was enabled at build time.\n";
}

Expand Down Expand Up @@ -6115,12 +6137,12 @@ print_help_end(Oiiotool& ot, std::ostream& out)
out << formatted_format_list("Input", "input_format_list") << "\n";
out << formatted_format_list("Output", "output_format_list") << "\n";

if (int ociover = ot.colorconfig.OpenColorIO_version_hex())
if (int ociover = ot.colorconfig().OpenColorIO_version_hex())
print(out, "OpenColorIO {}.{}.{}\n", (ociover >> 24),
((ociover >> 16) & 0xff), ((ociover >> 8) & 0xff));
else
print(out, "No OpenColorIO\n");
print(out, " Color config: {}\n", ot.colorconfig.configname());
print(out, " Color config: {}\n", ot.colorconfig().configname());
print(out, " Run `oiiotool --colorconfiginfo` for a "
"full color management inventory.\n");

Expand Down
4 changes: 3 additions & 1 deletion src/oiiotool/oiiotool.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ class Oiiotool {
std::vector<ImageRecRef> image_stack; // stack of previous images
std::map<std::string, ImageRecRef> image_labels; // labeled images
std::shared_ptr<ImageCache> imagecache; // back ptr to ImageCache
ColorConfig colorconfig; // OCIO color config
std::unique_ptr<ColorConfig> m_colorconfig; // OCIO color config
Timer total_runtime;
// total_readtime is the amount of time for direct reads, and does not
// count time spent inside ImageCache.
Expand Down Expand Up @@ -370,6 +370,8 @@ class Oiiotool {
// Merge stats from another Oiiotool
void merge_stats(const Oiiotool& ot);

ColorConfig& colorconfig();

private:
CallbackFunction m_pending_callback;
std::vector<const char*> m_pending_argv;
Expand Down
10 changes: 10 additions & 0 deletions testsuite/oiiotool-control/ref/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -176,69 +176,79 @@ Begin sequence iteration 0
copyB.#.jpg -> copyB.0001.jpg
Reading ./copyA.0001.jpg
Output: copyB.0001.jpg
oiiotool Creating ColorConfig
Writing copyB.0001.jpg

Begin sequence iteration 1
copyA.#.jpg -> ./copyA.0002.jpg
copyB.#.jpg -> copyB.0002.jpg
Reading ./copyA.0002.jpg
Output: copyB.0002.jpg
oiiotool Creating ColorConfig
Writing copyB.0002.jpg

Begin sequence iteration 2
copyA.#.jpg -> ./copyA.0003.jpg
copyB.#.jpg -> copyB.0003.jpg
Reading ./copyA.0003.jpg
Output: copyB.0003.jpg
oiiotool Creating ColorConfig
Writing copyB.0003.jpg

Begin sequence iteration 3
copyA.#.jpg -> ./copyA.0004.jpg
copyB.#.jpg -> copyB.0004.jpg
Reading ./copyA.0004.jpg
Output: copyB.0004.jpg
oiiotool Creating ColorConfig
Writing copyB.0004.jpg

Begin sequence iteration 4
copyA.#.jpg -> ./copyA.0005.jpg
copyB.#.jpg -> copyB.0005.jpg
Reading ./copyA.0005.jpg
Output: copyB.0005.jpg
oiiotool Creating ColorConfig
Writing copyB.0005.jpg

Begin sequence iteration 5
copyA.#.jpg -> ./copyA.0006.jpg
copyB.#.jpg -> copyB.0006.jpg
Reading ./copyA.0006.jpg
Output: copyB.0006.jpg
oiiotool Creating ColorConfig
Writing copyB.0006.jpg

Begin sequence iteration 6
copyA.#.jpg -> ./copyA.0007.jpg
copyB.#.jpg -> copyB.0007.jpg
Reading ./copyA.0007.jpg
Output: copyB.0007.jpg
oiiotool Creating ColorConfig
Writing copyB.0007.jpg

Begin sequence iteration 7
copyA.#.jpg -> ./copyA.0008.jpg
copyB.#.jpg -> copyB.0008.jpg
Reading ./copyA.0008.jpg
Output: copyB.0008.jpg
oiiotool Creating ColorConfig
Writing copyB.0008.jpg

Begin sequence iteration 8
copyA.#.jpg -> ./copyA.0009.jpg
copyB.#.jpg -> copyB.0009.jpg
Reading ./copyA.0009.jpg
Output: copyB.0009.jpg
oiiotool Creating ColorConfig
Writing copyB.0009.jpg

Begin sequence iteration 9
copyA.#.jpg -> ./copyA.0010.jpg
copyB.#.jpg -> copyB.0010.jpg
Reading ./copyA.0010.jpg
Output: copyB.0010.jpg
oiiotool Creating ColorConfig
Writing copyB.0010.jpg

copyA.0001.jpg : 128 x 96, 3 channel, uint8 jpeg
Expand Down

0 comments on commit fe9f7f8

Please sign in to comment.