diff --git a/src/ui/Application.cpp b/src/ui/Application.cpp index c59d37c..b324465 100644 --- a/src/ui/Application.cpp +++ b/src/ui/Application.cpp @@ -208,6 +208,7 @@ bool Application::Initialize(const std::string& windowTitle, int width, int heig m_prefsPath = (dir / "preferences.ini").string(); m_viewportPanel->LoadSettings(m_viewportSettingsPath); LoadPreferences(); + ValidateOcioPreferences(); // Apply pref delegate only to tiles that have no saved delegate. m_viewportPanel->ApplyDefaultDelegate(m_prefs.renderDelegate); // Color correction is always global — apply to all tiles. @@ -480,6 +481,76 @@ void Application::LoadPreferences() } } +void Application::ValidateOcioPreferences() +{ + // A persisted display/view that doesn't exist in the active OCIO config + // makes HdxColorCorrectionTask throw ("Display 'x' not found") every frame + // and silently skip correction, which reads as a washed-out viewport -- + // worst on delegates like hdEmbree whose output depends on the transform. + // Note display and view are easy to invert: in the bundled ACES 1.2 config + // the only display is "ACES" and "sRGB" is one of its views. + const OcioConfig& cfg = GetCurrentOcioConfig(); + if (!cfg.valid) { + // No $OCIO / unreadable config -- nothing to validate against. Leave + // the prefs alone rather than clobbering values that may be correct + // for a config supplied later. + return; + } + + auto contains = [](const std::vector& v, const std::string& s) { + return std::find(v.begin(), v.end(), s) != v.end(); + }; + + // --- display --- + if (m_prefs.ocioDisplay.empty() || !contains(cfg.displays, m_prefs.ocioDisplay)) { + if (!m_prefs.ocioDisplay.empty()) { + LOG_WARNING("OCIO display '" + m_prefs.ocioDisplay + + "' not in config; falling back to '" + + cfg.defaultDisplay + "'"); + } + m_prefs.ocioDisplay = cfg.defaultDisplay; + } + + // --- view (must belong to the display resolved above) --- + auto viewsIt = cfg.views.find(m_prefs.ocioDisplay); + const std::vector* views = + (viewsIt != cfg.views.end()) ? &viewsIt->second : nullptr; + + if (views && !views->empty()) { + if (m_prefs.ocioView.empty() || !contains(*views, m_prefs.ocioView)) { + // Prefer the config default when it's valid for this display, + // otherwise take the display's first view. + const std::string fallback = + contains(*views, cfg.defaultView) ? cfg.defaultView : views->front(); + if (!m_prefs.ocioView.empty()) { + LOG_WARNING("OCIO view '" + m_prefs.ocioView + + "' not valid for display '" + m_prefs.ocioDisplay + + "'; falling back to '" + fallback + "'"); + } + m_prefs.ocioView = fallback; + } + } + + // --- color space (same failure mode if it names a missing space) --- + if (!m_prefs.ocioColorSpace.empty() + && !contains(cfg.colorSpaces, m_prefs.ocioColorSpace)) { + LOG_WARNING("OCIO color space '" + m_prefs.ocioColorSpace + + "' not in config; clearing it"); + m_prefs.ocioColorSpace.clear(); + } + + // --- look --- + if (!m_prefs.ocioLook.empty() + && !contains(cfg.looks, m_prefs.ocioLook)) { + LOG_WARNING("OCIO look '" + m_prefs.ocioLook + + "' not in config; clearing it"); + m_prefs.ocioLook.clear(); + } + + LOG_INFO("OCIO display/view: '" + m_prefs.ocioDisplay + "' / '" + + m_prefs.ocioView + "'"); +} + void Application::SavePreferences() { // Pull the latest splitter positions; called from Shutdown before the diff --git a/src/ui/Application.h b/src/ui/Application.h index c58a0aa..dd74cbb 100644 --- a/src/ui/Application.h +++ b/src/ui/Application.h @@ -79,6 +79,10 @@ private: // Preferences void LoadPreferences(); void SavePreferences(); + /// Reconcile persisted OCIO display/view names against the active config. + /// Falls back to the config defaults when a name doesn't exist, so a stale + /// or mistyped preference can't silently disable color correction. + void ValidateOcioPreferences(); void ApplyPrefsToAllViewports(); void RenderPreferencesDialog();