#include "OCIOConfigManager.h" #include #include #include #include #include //------------------------------------------------------------------------------ void OCIOConfigObserver::notifyConfigChanged() { if (_configChangedCallback) { _configChangedCallback(); } }; void OCIOConfigObserver::setConfigChangedCallback(ConfigChangedCallback callback) { _configChangedCallback = callback; } OCIOObservable::OCIOObservable() : _ocioObservers() { } std::shared_ptr OCIOObservable::createObserver() { // Note: use of make_shared will result in observer object getting created // with shared_ptr control block in a single memory allocation. This means // that the observer memory will not get released until all shared_ptrs AND // all weak_ptrs that point to the observer are destroyed. auto observer = std::shared_ptr(new OCIOConfigObserver()); _ocioObservers.push_back(std::weak_ptr(observer)); return observer; } void OCIOObservable::notifyAllConfigChanged() { auto observerIter = _ocioObservers.begin(); while (observerIter != _ocioObservers.end()) { auto& observer = *observerIter; auto sharedObserver = observer.lock(); if (sharedObserver) { sharedObserver->notifyConfigChanged(); ++observerIter; } else { // Last shared_ptr to this observer has been destroyed. Remove the // dangling weak_ptr (the observer object should have already been // destroyed). observerIter = _ocioObservers.erase(observerIter); } } } OCIOConfigManager::OCIOConfigManager(): _ocioConfigPathMap() { } OCIO::ConstConfigRcPtr OCIOConfigManager::loadConfig(const std::string& configPath) { mFnAssert(!configPath.empty()); const bool addNewConfig = !hasConfig(configPath); if (addNewConfig) { addConfig(configPath); } return getConfig(configPath); } OCIO::ConstConfigRcPtr OCIOConfigManager::reloadConfig(const std::string& configPath) { addConfig(configPath); return getConfig(configPath); } bool OCIOConfigManager::hasConfig(const std::string& configPath) const { mFnAssert(!configPath.empty()); return (_ocioConfigPathMap.count(configPath) == 1); } void OCIOConfigManager::addConfig(const std::string& configPath) { mFnAssert(!configPath.empty()); try { OCIO::ConstConfigRcPtr config = OCIO::Config::CreateFromFile(configPath.c_str()); _ocioConfigPathMap[configPath] = { config, config->getCacheID() }; } catch (OCIO::Exception& exception) { _ocioConfigPathMap[configPath] = { OCIO::ConstConfigRcPtr(), std::string() }; } } OCIO::ConstConfigRcPtr OCIOConfigManager::getConfig(const std::string& configPath) const { mFnAssert(!configPath.empty()); mFnAssert(hasConfig(configPath)); const auto& configCachePair = _ocioConfigPathMap.at(configPath); return configCachePair.first; } /*static*/ std::shared_ptr NukeOCIOConfigManager::CreateObserver() { return Get()._ocioSubject->createObserver(); } /*static*/ NukeOCIOConfigManager& NukeOCIOConfigManager::Get() { static NukeOCIOConfigManager* ocioConfigManager = new NukeOCIOConfigManager; return *ocioConfigManager; } /*static*/ OCIO::ConstConfigRcPtr NukeOCIOConfigManager::LoadConfig(const DD::Image::Op* op) { return Get().loadConfig(op); } NukeOCIOConfigManager::NukeOCIOConfigManager() : _ocioConfigManager() , _ocioSubject(std::make_unique()) { } OCIO::ConstConfigRcPtr NukeOCIOConfigManager::loadConfig(const DD::Image::Op* op) { // We have to register for changes here because its the only place we have access to the op registerForChangesOnRootNode(op); _configPath = getConfigPath(op); return _ocioConfigManager.loadConfig(_configPath); } std::string NukeOCIOConfigManager::getConfigPath(const DD::Image::Op* op) const { const DD::Image::Op* rootOp = op->rootOp(); mFnAssert(rootOp); const DD::Image::Knob* ocioConfigKnob = rootOp->knob("OCIOConfigPath"); mFnAssert(ocioConfigKnob); // Pass in a dummy OutputContext to get_text(), so that the File_Knob remaps the path it returns if needed. const DD::Image::OutputContext outputContext; const char* configPath = ocioConfigKnob->get_text(&outputContext); mFnAssert(configPath); return std::string(configPath); } void NukeOCIOConfigManager::registerForChangesOnRootNode(const DD::Image::Op* op) { const auto root = op->rootOp()->getNode(); root->registerKnobChangedObserver(this); } void NukeOCIOConfigManager::observedKnobChanged(DD::Image::Knob* knob) { if (knob->is("reloadConfig")) { _ocioConfigManager.reloadConfig(_configPath); } else if (knob->is("colorManagement") || knob->is("OCIOConfigPath")) { // TODO Observers will get callbacks for all root nodes that have been // registered for knob changed events in registerForChangesOnRootNode(). // This is because we never call unregisterKnobChangedObserver() for // root nodes. As a result, observers may need to check if the config // returned by loadConfig() has actually changed in their config changed // callback. _ocioSubject->notifyAllConfigChanged(); } }