Repository: AcademySoftwareFoundation/OpenColorIO
Pull request #2083: Add view helper functions to Config class
Author: login: annieln
Labels: name: devdays25
Created: 2024-10-17T21:40:05Z | Merged: 2025-05-26T18:12:05Z

#2049
#2082 


Diff:
diff --git a/include/OpenColorIO/OpenColorIO.h b/include/OpenColorIO/OpenColorIO.h
index c5e499ff78..5ef81b8112 100644
--- a/include/OpenColorIO/OpenColorIO.h
+++ b/include/OpenColorIO/OpenColorIO.h
@@ -852,6 +852,10 @@ class OCIOEXPORT Config
      * \ref Config::validate will throw if the config does not contain
      * the matching display color space.
      */
+
+    /// Check if a view within a given display is referencing one of the config's shared views.
+    bool viewIsShared(const char * dispName, const char * viewName) const;
+
     /// Will throw if view or colorSpaceName are null or empty.
     void addSharedView(const char * view, const char * viewTransformName,
                        const char * colorSpaceName, const char * looks,
@@ -859,6 +863,9 @@ class OCIOEXPORT Config
     /// Remove a shared view.  Will throw if the view does not exist.
     void removeSharedView(const char * view);
 
+    /// Clear all shared views. This will throw if any displays are still using the shared views.
+    void clearSharedViews();
+
     const char * getDefaultDisplay() const;
     int getNumDisplays() const;
     /// Will return "" if the index is invalid.
@@ -883,6 +890,23 @@ class OCIOEXPORT Config
     int getNumViews(const char * display, const char * colorspaceName) const;
     const char * getView(const char * display, const char * colorspaceName, int index) const;
 
+    /**
+     * \brief Compare views in a pair of configs.
+     *
+     * Will return false if either of the views does not exist. This will return true even
+     * if the view is display-defined in one config and a reference to a shared view in the
+     * other config (both within the same display), as long as the contents match. The
+     * description text (if any) is ignored, since it is not a functional difference.
+     * 
+     * Note that the comparison is only on the strings contained in the view definition,
+     * the function does not attempt to compare that the color spaces or view transforms
+     * being referenced are identical (only that they have the same name).
+     */
+    static bool ViewsAreEqual(const ConstConfigRcPtr & first,
+                              const ConstConfigRcPtr & second,
+                              const char * dispName,
+                              const char * viewName);
+
     /**
      * Returns the view_transform attribute of the (display, view) pair. View can
      * be a shared view of the display. If display is null or empty, config shared views are used.
@@ -900,6 +924,17 @@ class OCIOEXPORT Config
     /// Returns the description attribute of a (display, view) pair.
     const char * getDisplayViewDescription(const char * display, const char * view) const noexcept;
 
+    /**
+     * \brief Determine if a display and view exist.
+     *
+     * This returns false if either the display or view doesn't exist. It works regardless
+     * of whether the display or view are active, and it works regardless of whether the
+     * view is display-defined or if the display has this as a shared view. It will only
+     * check config-level shared views if dispName is null. It will not check config level
+     * shared views if dispName is not null.
+     */
+    bool displayHasView(const char * dispName, const char * viewName) const;
+
     /**
      * For the (display, view) pair, specify which color space and look to use.
      * If a look is not desired, then just pass a null or empty string.
@@ -963,6 +998,18 @@ class OCIOEXPORT Config
      *
      */
 
+    /**
+     * \brief Determine if a virtual view exists.
+     *
+     * This returns false if the virtual view doesn't exist. It works regardless of
+     * whether the virtual view is active, and it works regardless of whether the virtual
+     * view is display-defined or if the display has this as a shared virtual view.
+     */
+    bool hasVirtualView(const char * viewName) const;
+
+    /// Check if a given virtual view is referencing one of the config's shared views.
+    bool virtualViewIsShared(const char * viewName) const;
+
     void addVirtualDisplayView(const char * view,
                                const char * viewTransformName,
                                const char * colorSpaceName,
@@ -977,6 +1024,23 @@ class OCIOEXPORT Config
     /// Get the view name at a specific index.
     const char * getVirtualDisplayView(ViewType type, int index) const noexcept;
 
+    /**
+     * \brief Compare virtual views in a pair of configs.
+     *
+     * Will return false if either of the virtual views does not exist. This will return true
+     * even if the virtual view is display-defined in one config and a reference to a shared
+     * virtual view in the other config, as long as the contents match.
+     * 
+     * The description text (if any) is ignored, since it is not a functional difference.
+     * 
+     * Note that the comparison is only on the strings contained in the view definition,
+     * the function does not attempt to compare that the color spaces or view transforms
+     * being referenced are identical (only that they have the same name).
+     */
+    static bool VirtualViewsAreEqual(const ConstConfigRcPtr & first,
+                                     const ConstConfigRcPtr & second,
+                                     const char * viewName);
+
     const char * getVirtualDisplayViewTransformName(const char * view) const noexcept;
     const char * getVirtualDisplayViewColorSpaceName(const char * view) const noexcept;
     const char * getVirtualDisplayViewLooks(const char * view) const noexcept;
diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp
index a4cf7c5fe4..c44b2fdc34 100644
--- a/src/OpenColorIO/Config.cpp
+++ b/src/OpenColorIO/Config.cpp
@@ -3305,6 +3305,22 @@ void Config::setViewingRules(ConstViewingRulesRcPtr viewingRules)
     getImpl()->resetCacheIDs();
 }
 
+bool Config::viewIsShared(const char * dispName, const char * viewName) const
+{
+    if (!viewName || !*viewName) return false;
+
+    for (int v = 0; v < getNumViews(VIEW_SHARED, dispName); v++)
+    {
+        const char * sharedViewName = getView(VIEW_SHARED, dispName, v);
+        if (sharedViewName && *sharedViewName && Platform::Strcasecmp(sharedViewName, viewName) == 0)
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 void Config::addSharedView(const char * view, const char * viewTransform,
                            const char * colorSpace, const char * looks,
                            const char * rule, const char * description)
@@ -3358,6 +3374,19 @@ void Config::removeSharedView(const char * view)
     }
 }
 
+void Config::clearSharedViews()
+{
+    int numViews = getNumViews(VIEW_SHARED, nullptr);
+    for (int v = numViews - 1; v >= 0; v--)
+    {
+        const char * sharedViewName = getView(VIEW_SHARED, nullptr, v);
+        if (sharedViewName && *sharedViewName)
+        {
+            removeSharedView(sharedViewName);
+        }
+    }
+}
+
 const char * Config::getDefaultDisplay() const
 {
     return getDisplay(0);
@@ -3487,6 +3516,43 @@ const char * Config::getView(const char * display, const char * colorspace, int
     return "";
 }
 
+
+bool Config::ViewsAreEqual(const ConstConfigRcPtr & first,
+                           const ConstConfigRcPtr & second,
+                           const char * dispName,               // may be empty or nullptr for shared views
+                           const char * viewName)
+{
+    // It's ok to call this even for displays/views that don't exist, it will simply return false.
+
+    // Note that this will return true even if the view is display-defined in one config and a reference
+    // to a shared view in the other config (both within the same display), as long as the contents match.
+
+    // These calls return null if either the display or view doesn't exist (regardless if it's active).
+    const char * cs1 = first->getDisplayViewColorSpaceName(dispName, viewName);
+    const char * cs2 = second->getDisplayViewColorSpaceName(dispName, viewName);
+
+    // If the color space is not null, the display and view exist.
+    if (cs1 && *cs1 && cs2 && *cs2)
+    {
+        // Both configs have a display and view by this name, now check the contents.
+        if (Platform::Strcasecmp(cs1, cs2) == 0)
+        {
+            // Note the remaining strings may be empty in a valid view.
+            // Intentionally not checking the description since it is not a functional difference.
+            if ( (Platform::Strcasecmp(first->getDisplayViewLooks(dispName, viewName),
+                                       second->getDisplayViewLooks(dispName, viewName)) == 0) &&
+                 (Platform::Strcasecmp(first->getDisplayViewTransformName(dispName, viewName),
+                                       second->getDisplayViewTransformName(dispName, viewName)) == 0) &&
+                 (Platform::Strcasecmp(first->getDisplayViewRule(dispName, viewName),
+                                       second->getDisplayViewRule(dispName, viewName)) == 0) )
+            {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 const char * Config::getDisplayViewTransformName(const char * display, const char * view) const
 {
     const View * viewPtr = getImpl()->getView(display, view);
@@ -3525,6 +3591,21 @@ const char * Config::getDisplayViewDescription(const char * display, const char
     return viewPtr ? viewPtr->m_description.c_str() : "";
 }
 
+bool Config::displayHasView(const char * dispName, const char * viewName) const
+{
+    // This returns null if either the display or view doesn't exist.
+    // It works regardless of whether the display or view are active,
+    // and it works regardless of whether the view is display-defined
+    // or if the display has this as a shared view.
+    //
+    // It will only check config level shared views if dispName is null.
+    // It will not check config level shared views if dispName is not null.
+    const char * cs = getDisplayViewColorSpaceName(dispName, viewName);
+
+    // All views must have a color space, so if it's not empty, the view exists.
+    return (cs && *cs);
+}
+
 void Config::addDisplaySharedView(const char * display, const char * sharedView)
 {
     if (!display || !*display)
@@ -3695,6 +3776,30 @@ void Config::clearDisplays()
     getImpl()->resetCacheIDs();
 }
 
+bool Config::hasVirtualView(const char * viewName) const
+{
+    const char * cs = getVirtualDisplayViewColorSpaceName(viewName);
+
+    // All views must have a color space, so if it's not empty, the view exists.
+    return (cs && *cs);
+}
+
+bool Config::virtualViewIsShared(const char * viewName) const
+{
+    if (!viewName || !*viewName) return false;
+
+    for (int v = 0; v < getVirtualDisplayNumViews(VIEW_SHARED); v++)
+    {
+        const char * sharedViewName = getVirtualDisplayView(VIEW_SHARED, v);
+        if (sharedViewName && *sharedViewName && Platform::Strcasecmp(sharedViewName, viewName) == 0)
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 void Config::addVirtualDisplayView(const char * view,
                                    const char * viewTransform,
                                    const char * colorSpace,
@@ -3795,10 +3900,44 @@ const char * Config::getVirtualDisplayView(ViewType type, int index) const noexc
     return "";
 }
 
+bool Config::VirtualViewsAreEqual(const ConstConfigRcPtr & first,
+                                  const ConstConfigRcPtr & second,
+                                  const char * viewName)
+{
+    const char * cs1 = first->getVirtualDisplayViewColorSpaceName(viewName);
+    const char * cs2 = second->getVirtualDisplayViewColorSpaceName(viewName);
+
+    // If the color space is not null, the display and view exist.
+    if (cs1 && *cs1 && cs2 && *cs2)
+    {
+        if (Platform::Strcasecmp(cs1, cs2) == 0)
+        {
+            // Note the remaining strings may be empty in a valid view.
+            // Intentionally not checking the description since it is not a functional difference.
+            if ( (Platform::Strcasecmp(first->getVirtualDisplayViewLooks(viewName),
+                                       second->getVirtualDisplayViewLooks(viewName)) == 0) &&
+                 (Platform::Strcasecmp(first->getVirtualDisplayViewTransformName(viewName),
+                                       second->getVirtualDisplayViewTransformName(viewName)) == 0) &&
+                 (Platform::Strcasecmp(first->getVirtualDisplayViewRule(viewName),
+                                       second->getVirtualDisplayViewRule(viewName)) == 0) )
+            {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 const char * Config::getVirtualDisplayViewTransformName(const char * view) const noexcept
 {
     if (!view) return "";
 
+    // Get the view transform name for the case where a virtual view is shared.
+    if (virtualViewIsShared(view))
+    {
+        return getDisplayViewTransformName(nullptr, view);
+    }
+
     ViewVec::const_iterator iter = FindView(getImpl()->m_virtualDisplay.m_views, view);
     if (iter != getImpl()->m_virtualDisplay.m_views.end())
     {
@@ -3812,6 +3951,12 @@ const char * Config::getVirtualDisplayViewColorSpaceName(const char * view) cons
 {
     if (!view) return "";
 
+    // Get the colorspace name for the case where a virtual view is shared.
+    if (virtualViewIsShared(view))
+    {
+        return getDisplayViewColorSpaceName(nullptr, view);
+    }
+
     ViewVec::const_iterator iter = FindView(getImpl()->m_virtualDisplay.m_views, view);
     if (iter != getImpl()->m_virtualDisplay.m_views.end())
     {
@@ -3825,6 +3970,12 @@ const char * Config::getVirtualDisplayViewLooks(const char * view) const noexcep
 {
     if (!view) return "";
 
+    // Get the view looks for the case where a virtual view is shared
+    if (virtualViewIsShared(view))
+    {
+        return getDisplayViewLooks(nullptr, view);
+    }
+
     ViewVec::const_iterator iter = FindView(getImpl()->m_virtualDisplay.m_views, view);
     if (iter != getImpl()->m_virtualDisplay.m_views.end())
     {
@@ -3838,6 +3989,12 @@ const char * Config::getVirtualDisplayViewRule(const char * view) const noexcept
 {
     if (!view) return "";
 
+    // Get the view rule for the case where a virtual view is shared
+    if (virtualViewIsShared(view))
+    {
+        return getDisplayViewRule(nullptr, view);
+    }
+
     ViewVec::const_iterator iter = FindView(getImpl()->m_virtualDisplay.m_views, view);
     if (iter != getImpl()->m_virtualDisplay.m_views.end())
     {
@@ -3851,6 +4008,12 @@ const char * Config::getVirtualDisplayViewDescription(const char * view) const n
 {
     if (!view) return "";
 
+    // Get the view description for the case where a virtual view is shared
+    if (virtualViewIsShared(view))
+    {
+        return getDisplayViewDescription(nullptr, view);
+    }
+
     ViewVec::const_iterator iter = FindView(getImpl()->m_virtualDisplay.m_views, view);
     if (iter != getImpl()->m_virtualDisplay.m_views.end())
     {
diff --git a/src/bindings/python/PyConfig.cpp b/src/bindings/python/PyConfig.cpp
index 9534ddc358..1d24cd9dc7 100644
--- a/src/bindings/python/PyConfig.cpp
+++ b/src/bindings/python/PyConfig.cpp
@@ -404,6 +404,8 @@ void bindPyConfig(py::module & m)
              DOC(Config, addSharedView))
         .def("removeSharedView", &Config::removeSharedView, "view"_a, 
              DOC(Config, removeSharedView))
+        .def("clearSharedViews", &Config::clearSharedViews,
+             DOC(Config, clearSharedViews))
         .def("getSharedViews", [](ConfigRcPtr & self) 
             { 
                 return SharedViewIterator(self); 
@@ -456,6 +458,9 @@ void bindPyConfig(py::module & m)
         .def("getDisplayViewDescription", &Config::getDisplayViewDescription, 
              "display"_a, "view"_a, 
              DOC(Config, getDisplayViewDescription))
+        .def("displayHasView", &Config::displayHasView,
+             "display"_a, "view"_a,
+             DOC(Config, displayHasView))
         .def("addDisplayView", 
              (void (Config::*)(const char *, const char *, const char *, const char *)) 
              &Config::addDisplayView, 
@@ -475,20 +480,35 @@ void bindPyConfig(py::module & m)
              "ruleName"_a = "", 
              "description"_a = "", 
              DOC(Config, addDisplayView))
+        .def("viewIsShared", &Config::viewIsShared, "display"_a, "view"_a,
+             DOC(Config, viewIsShared))
         .def("addDisplaySharedView", &Config::addDisplaySharedView, "display"_a, "view"_a, 
              DOC(Config, addDisplaySharedView))
         .def("removeDisplayView", &Config::removeDisplayView, "display"_a, "view"_a, 
              DOC(Config, removeDisplayView))
         .def("clearDisplays", &Config::clearDisplays, 
              DOC(Config, clearDisplays))
+        .def_static("ViewsAreEqual", [](const ConstConfigRcPtr & first,
+                                        const ConstConfigRcPtr & second,
+                                        const char * dispName,
+                                        const char * viewName)
+            {
+                return Config::ViewsAreEqual(first, second, dispName, viewName);
+            },
+                    "first"_a, "second"_a, "dispName"_a, "viewName"_a,
+                    DOC(Config, ViewsAreEqual))
 
         // Virtual Display
+        .def("hasVirtualView", &Config::hasVirtualView, "view"_a,
+             DOC(Config, hasVirtualView))
         .def("addVirtualDisplayView", &Config::addVirtualDisplayView, 
              "view"_a, "viewTransformName"_a, "colorSpaceName"_a, 
              "looks"_a = "",
              "ruleName"_a = "", 
              "description"_a = "", 
              DOC(Config, addVirtualDisplayView))
+        .def("virtualViewIsShared", &Config::virtualViewIsShared, "view"_a,
+             DOC(Config, virtualViewIsShared))
         .def("addVirtualDisplaySharedView", &Config::addVirtualDisplaySharedView, "sharedView"_a,
              DOC(Config, addVirtualDisplaySharedView))
         .def("getVirtualDisplayViews", [](ConfigRcPtr & self, ViewType type)
@@ -532,6 +552,14 @@ void bindPyConfig(py::module & m)
                  return false;
              },
              "display"_a)
+        .def_static("VirtualViewsAreEqual", [](const ConstConfigRcPtr & first,
+                                               const ConstConfigRcPtr & second,
+                                               const char * viewName)
+            {
+                return Config::VirtualViewsAreEqual(first, second, viewName);
+            },
+                    "first"_a, "second"_a, "viewName"_a,
+                    DOC(Config, VirtualViewsAreEqual))
 
         // Active Displays and Views
         .def("setActiveDisplays", &Config::setActiveDisplays, "displays"_a, 
diff --git a/tests/cpu/Config_tests.cpp b/tests/cpu/Config_tests.cpp
index 518b97275b..45145f9de0 100644
--- a/tests/cpu/Config_tests.cpp
+++ b/tests/cpu/Config_tests.cpp
@@ -6902,17 +6902,28 @@ OCIO_ADD_TEST(Config, display_view)
 
     // Add a simple view.
     const std::string display{ "display" };
+
+    OCIO_CHECK_ASSERT(!config->displayHasView(display.c_str(), "view1"));
+
     OCIO_CHECK_NO_THROW(config->addDisplayView(display.c_str(), "view1", "scs", ""));
 
+    OCIO_CHECK_ASSERT(config->displayHasView(display.c_str(), "view1"));
+
     OCIO_CHECK_NO_THROW(config->validate());
 
+    OCIO_CHECK_ASSERT(!config->displayHasView(display.c_str(), "view2"));
+
     OCIO_CHECK_NO_THROW(config->addDisplayView(display.c_str(), "view2", "view_transform", "scs",
                                                "", "", ""));
     OCIO_CHECK_THROW_WHAT(config->validate(), OCIO::Exception,
                           "color space, 'scs', that is not a display-referred");
 
+    OCIO_CHECK_ASSERT(config->displayHasView(display.c_str(), "view2"));
+
     OCIO_CHECK_NO_THROW(config->addDisplayView(display.c_str(), "view2", "view_transform", "dcs",
                                                "", "", ""));
+    OCIO_CHECK_ASSERT(config->displayHasView(display.c_str(), "view2"));
+
     OCIO_CHECK_NO_THROW(config->validate());
 
     // Validate how the config is serialized.
@@ -7339,6 +7350,7 @@ OCIO_ADD_TEST(Config, add_remove_display)
     // Add a (display, view) pair.
 
     OCIO_CHECK_NO_THROW(config->addDisplayView("disp1", "view1", "raw", nullptr));
+    OCIO_CHECK_ASSERT(config->displayHasView("disp1", "view1"));
     OCIO_REQUIRE_EQUAL(config->getNumDisplays(), 2);
     OCIO_CHECK_EQUAL(std::string(config->getDisplay(0)), std::string("sRGB"));
     OCIO_CHECK_EQUAL(std::string(config->getDisplay(1)), std::string("disp1"));
@@ -7347,6 +7359,7 @@ OCIO_ADD_TEST(Config, add_remove_display)
     // Remove a (display, view) pair.
 
     OCIO_CHECK_NO_THROW(config->removeDisplayView("disp1", "view1"));
+    OCIO_CHECK_ASSERT(!config->displayHasView("disp1", "view1"));
     OCIO_REQUIRE_EQUAL(config->getNumDisplays(), 1);
     OCIO_CHECK_EQUAL(std::string(config->getDisplay(0)), std::string("sRGB"));
 }
@@ -8489,15 +8502,30 @@ active_views: []
     OCIO_CHECK_EQUAL(2, config->getNumViews(OCIO::VIEW_DISPLAY_DEFINED, "sRGB"));
     OCIO_CHECK_EQUAL(1, config->getNumViews(OCIO::VIEW_SHARED, "sRGB"));
 
+    OCIO_CHECK_EQUAL(std::string("sview1"), config->getView(OCIO::VIEW_SHARED, "sRGB", 0));
+    OCIO_CHECK_ASSERT(config->displayHasView("sRGB", "sview1"));
+    OCIO_CHECK_ASSERT(config->viewIsShared("sRGB", "sview1"));
+    OCIO_CHECK_ASSERT(!config->viewIsShared("sRGB", ""));
+    OCIO_CHECK_ASSERT(!config->viewIsShared("sRGB", nullptr));
+
+    OCIO_CHECK_EQUAL(std::string("raw"), config->getDisplayViewColorSpaceName("sRGB", "sview1"));
+    OCIO_CHECK_EQUAL(std::string("raw"), config->getVirtualDisplayViewColorSpaceName("sview2"));
+
     // Step 3 - Validate the virtual display information.
 
     {
         OCIO::ConfigRcPtr cfg = config->createEditableCopy();
 
+        OCIO_CHECK_ASSERT(OCIO::Config::ViewsAreEqual(config, cfg, "sRGB", "sview1"));
+        OCIO_CHECK_ASSERT(OCIO::Config::ViewsAreEqual(config, cfg, "sRGB", "Raw"));
+        OCIO_CHECK_ASSERT(OCIO::Config::ViewsAreEqual(config, cfg, "sRGB", "view"));
+
         OCIO_REQUIRE_EQUAL(2, cfg->getVirtualDisplayNumViews(OCIO::VIEW_DISPLAY_DEFINED));
 
         const char * viewName = cfg->getVirtualDisplayView(OCIO::VIEW_DISPLAY_DEFINED, 0);
 
+        OCIO_CHECK_ASSERT(cfg->hasVirtualView(viewName));
+
         OCIO_CHECK_EQUAL(std::string("Raw"), viewName);
         OCIO_CHECK_EQUAL(std::string(""), cfg->getVirtualDisplayViewTransformName(viewName));
         OCIO_CHECK_EQUAL(std::string("raw"), cfg->getVirtualDisplayViewColorSpaceName(viewName));
@@ -8505,9 +8533,11 @@ active_views: []
         OCIO_CHECK_EQUAL(std::string(""), cfg->getVirtualDisplayViewRule(viewName));
         OCIO_CHECK_EQUAL(std::string(""), cfg->getVirtualDisplayViewDescription(viewName));
 
+        OCIO_CHECK_ASSERT(OCIO::Config::VirtualViewsAreEqual(config, cfg, viewName));
+
         viewName = cfg->getVirtualDisplayView(OCIO::VIEW_DISPLAY_DEFINED, 1);
 
-        OCIO_CHECK_EQUAL(std::string("Film"), cfg->getVirtualDisplayView(OCIO::VIEW_DISPLAY_DEFINED, 1));
+        OCIO_CHECK_EQUAL(std::string("Film"), viewName);
         OCIO_CHECK_EQUAL(std::string("display_vt"), cfg->getVirtualDisplayViewTransformName(viewName));
         OCIO_CHECK_EQUAL(std::string("<USE_DISPLAY_NAME>"), cfg->getVirtualDisplayViewColorSpaceName(viewName));
         OCIO_CHECK_EQUAL(std::string(""), cfg->getVirtualDisplayViewLooks(viewName));
@@ -8516,11 +8546,22 @@ active_views: []
 
         OCIO_REQUIRE_EQUAL(1, cfg->getVirtualDisplayNumViews(OCIO::VIEW_SHARED));
         OCIO_CHECK_EQUAL(std::string("sview2"), cfg->getVirtualDisplayView(OCIO::VIEW_SHARED, 0));
+        OCIO_CHECK_EQUAL(std::string("raw"), cfg->getVirtualDisplayViewColorSpaceName("sview2"));
+
+        OCIO_CHECK_ASSERT(cfg->hasVirtualView("sview2"));
+        OCIO_CHECK_ASSERT(cfg->virtualViewIsShared("sview2"));
+        OCIO_CHECK_ASSERT(OCIO::Config::VirtualViewsAreEqual(config, cfg, "sview2"));
+
+        OCIO_CHECK_ASSERT(!cfg->virtualViewIsShared(""));
+        OCIO_CHECK_ASSERT(!cfg->virtualViewIsShared(nullptr));
 
         // Remove a view from the Virtual Display.
 
         cfg->removeVirtualDisplayView("Raw");
 
+        OCIO_CHECK_ASSERT(!OCIO::Config::VirtualViewsAreEqual(config, cfg, "Raw"));
+        OCIO_CHECK_ASSERT(!cfg->hasVirtualView("Raw"));
+
         OCIO_REQUIRE_EQUAL(1, cfg->getVirtualDisplayNumViews(OCIO::VIEW_DISPLAY_DEFINED));
         OCIO_CHECK_EQUAL(std::string("Film"), cfg->getVirtualDisplayView(OCIO::VIEW_DISPLAY_DEFINED, 0));
 
@@ -8532,6 +8573,9 @@ active_views: []
         cfg->removeVirtualDisplayView("sview2");
         OCIO_REQUIRE_EQUAL(1, cfg->getVirtualDisplayNumViews(OCIO::VIEW_DISPLAY_DEFINED));
         OCIO_REQUIRE_EQUAL(0, cfg->getVirtualDisplayNumViews(OCIO::VIEW_SHARED));
+        OCIO_CHECK_ASSERT(!cfg->hasVirtualView("sview2"));
+        OCIO_CHECK_ASSERT(!cfg->virtualViewIsShared("sview2"));
+        OCIO_CHECK_ASSERT(!OCIO::Config::VirtualViewsAreEqual(config, cfg, "sview2"));
 
         {
             // Extra serialize & deserialize validation.
@@ -8552,12 +8596,14 @@ active_views: []
         cfg->addVirtualDisplaySharedView("sview2");
         OCIO_REQUIRE_EQUAL(1, cfg->getVirtualDisplayNumViews(OCIO::VIEW_DISPLAY_DEFINED));
         OCIO_REQUIRE_EQUAL(1, cfg->getVirtualDisplayNumViews(OCIO::VIEW_SHARED));
+        OCIO_CHECK_ASSERT(cfg->virtualViewIsShared("sview2"));
 
         // Remove the Virtual Display.
 
         cfg->clearVirtualDisplay();
         OCIO_REQUIRE_EQUAL(0, cfg->getVirtualDisplayNumViews(OCIO::VIEW_DISPLAY_DEFINED));
         OCIO_REQUIRE_EQUAL(0, cfg->getVirtualDisplayNumViews(OCIO::VIEW_SHARED));
+        OCIO_CHECK_ASSERT(!cfg->virtualViewIsShared("sview2"));
 
         {
             // Extra serialize & deserialize validation.
diff --git a/tests/cpu/Display_tests.cpp b/tests/cpu/Display_tests.cpp
index c9562aa0a2..03ef992ba8 100644
--- a/tests/cpu/Display_tests.cpp
+++ b/tests/cpu/Display_tests.cpp
@@ -232,4 +232,532 @@ OCIO_ADD_TEST(SharedViews, basic)
 
     OCIO_CHECK_EQUAL(std::string(OCIO::OCIO_VIEW_USE_DISPLAY_NAME),
                      configBack->getDisplayViewColorSpaceName(nullptr, "shared3"));
+
+    // Remove all shared views
+    OCIO_REQUIRE_EQUAL(4, config->getNumViews(OCIO::VIEW_SHARED, nullptr));
+    OCIO_CHECK_NO_THROW(config->clearSharedViews());
+    OCIO_REQUIRE_EQUAL(0, config->getNumViews(OCIO::VIEW_SHARED, nullptr));
+}
+
+OCIO_ADD_TEST(Config, compare_displays) {
+    static constexpr char CONFIG1[]{ R"(ocio_profile_version: 2
+
+roles:
+  default: raw
+
+file_rules:
+  - !<Rule> {name: Default, colorspace: default}
+
+shared_views:
+  - !<View> {name: sview1, colorspace: raw}
+
+displays:
+  Raw:
+    - !<View> {name: Raw, colorspace: raw}
+  sRGB:
+    - !<View> {name: Raw, colorspace: raw}
+    - !<View> {name: view, view_transform: display_vt, display_colorspace: display_cs}
+    - !<Views> [sview1]
+
+active_displays: [sRGB]
+active_views: [view, sview1]
+
+view_transforms:
+  - !<ViewTransform>
+    name: default_vt
+    to_scene_reference: !<CDLTransform> {sat: 1.5}
+
+  - !<ViewTransform>
+    name: display_vt
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+display_colorspaces:
+  - !<ColorSpace>
+    name: display_cs
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+colorspaces:
+  - !<ColorSpace>
+    name: raw
+)" };
+
+    static constexpr char CONFIG2[]{ R"(ocio_profile_version: 2
+
+roles:
+  default: raw
+
+file_rules:
+  - !<Rule> {name: Default, colorspace: default}
+
+shared_views:
+  - !<View> {name: view, view_transform: display_vt, display_colorspace: display_cs}
+  - !<View> {name: sview1, colorspace: raw}
+
+displays:
+  Raw:
+    - !<View> {name: Raw, colorspace: raw}
+  sRGB:
+    - !<View> {name: Raw, colorspace: raw}
+    - !<Views> [view, sview1]
+
+active_displays: [Raw]
+active_views: [Raw]
+
+view_transforms:
+  - !<ViewTransform>
+    name: default_vt
+    to_scene_reference: !<CDLTransform> {sat: 1.5}
+
+  - !<ViewTransform>
+    name: display_vt
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+display_colorspaces:
+  - !<ColorSpace>
+    name: display_cs
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+colorspaces:
+  - !<ColorSpace>
+    name: raw
+)" };
+
+    std::istringstream is;
+    is.str(CONFIG1);
+    OCIO::ConstConfigRcPtr config1, config2;
+    OCIO_CHECK_NO_THROW(config1 = OCIO::Config::CreateFromStream(is));
+    is.clear();
+    is.str(CONFIG2);
+    OCIO_CHECK_NO_THROW(config2 = OCIO::Config::CreateFromStream(is));
+    OCIO_CHECK_NO_THROW(config1->validate());
+    OCIO_CHECK_NO_THROW(config2->validate());
+
+    {
+        // Test that Config::ViewsAreEqual works for a matching (display, view) pair across separate configs.
+        // Works regardless of if the view is display-defined in one config and shared in the other.
+        // Works regardless of if the (display, view) pair is active in one config and inactive in another.
+
+        // Active (display, view) pair where the view is display-defined.
+        OCIO_REQUIRE_EQUAL(1, config1->getNumDisplays());
+        OCIO_REQUIRE_EQUAL(std::string("sRGB"), config1->getDefaultDisplay());
+
+        OCIO_CHECK_EQUAL(2, config1->getNumViews(OCIO::VIEW_DISPLAY_DEFINED, "sRGB"));
+        OCIO_CHECK_EQUAL(std::string("Raw"), config1->getView(OCIO::VIEW_DISPLAY_DEFINED, "sRGB", 0));
+        OCIO_CHECK_EQUAL(std::string("view"), config1->getView(OCIO::VIEW_DISPLAY_DEFINED, "sRGB", 1));
+
+        OCIO_CHECK_ASSERT(!config1->viewIsShared("sRGB", "view"));
+
+        OCIO_CHECK_EQUAL(2, config1->getNumViews("sRGB"));
+        OCIO_CHECK_EQUAL(std::string("view"), config1->getView("sRGB", 0));
+
+        // Inactive (display, view) pair where the view is a reference to a shared view.
+        OCIO_REQUIRE_EQUAL(1, config2->getNumDisplays());
+        OCIO_CHECK_EQUAL(std::string("Raw"), config2->getDefaultDisplay());
+        OCIO_CHECK_EQUAL(1, config2->getNumViews("Raw"));
+        OCIO_CHECK_EQUAL(std::string("Raw"), config2->getDefaultView("Raw"));
+
+        OCIO_REQUIRE_EQUAL(config2->getNumDisplaysAll(), 2);
+        OCIO_CHECK_EQUAL(config2->getDisplayAll(1), std::string("sRGB"));
+
+        OCIO_CHECK_EQUAL(2, config2->getNumViews(OCIO::VIEW_SHARED, "sRGB"));
+        OCIO_CHECK_EQUAL(std::string("view"), config2->getView(OCIO::VIEW_SHARED, "sRGB", 0));
+        OCIO_CHECK_EQUAL(std::string("sview1"), config2->getView(OCIO::VIEW_SHARED, "sRGB", 1));
+
+        OCIO_CHECK_ASSERT(config2->viewIsShared("sRGB", "view"));
+
+        OCIO_CHECK_ASSERT(OCIO::Config::ViewsAreEqual(config1, config2, "sRGB", "view"));
+        
+    }
+
+    {
+        // Test that Config::ViewsAreEqual works for a matching (display, view) pair across separate configs,
+        // even if the pair is active in one config and inactive in another. Both views are display-defined.
+
+        // Inactive (display, view) pair where the view is display-defined.
+        OCIO_REQUIRE_EQUAL(1, config1->getNumDisplays());
+        OCIO_CHECK_EQUAL(std::string("sRGB"), config1->getDefaultDisplay());
+        OCIO_CHECK_EQUAL(config1->getDisplayAll(0), std::string("Raw"));
+        OCIO_CHECK_EQUAL(1, config1->getNumViews(OCIO::VIEW_DISPLAY_DEFINED, "Raw"));
+        OCIO_CHECK_EQUAL(std::string("Raw"), config1->getView(OCIO::VIEW_DISPLAY_DEFINED, "Raw", 0));
+        OCIO_CHECK_ASSERT(!config1->viewIsShared("Raw", "Raw"));
+
+        // Active (display, view) pair where the view is display-defined.
+        OCIO_REQUIRE_EQUAL(1, config2->getNumDisplays());
+        OCIO_CHECK_EQUAL(std::string("Raw"), config2->getDefaultDisplay());
+        OCIO_CHECK_EQUAL(1, config2->getNumViews(OCIO::VIEW_DISPLAY_DEFINED, "Raw"));
+        OCIO_CHECK_EQUAL(std::string("Raw"), config2->getView(OCIO::VIEW_DISPLAY_DEFINED, "Raw", 0));
+        OCIO_CHECK_ASSERT(!config2->viewIsShared("Raw", "Raw"));
+
+        OCIO_CHECK_ASSERT(OCIO::Config::ViewsAreEqual(config1, config2, "Raw", "Raw"));
+    }
+
+    {
+        // Test that Config::ViewsAreEqual works for a matching (display, view) pair across separate configs, even
+        // if the pair is active in one config and inactive in another. Both views are reference to a shared view.
+        
+        // Active (display, view) pair where the view is a reference to a shared view.
+        OCIO_CHECK_EQUAL(1, config1->getNumViews(OCIO::VIEW_SHARED, "sRGB"));
+        OCIO_CHECK_EQUAL(std::string("sview1"), config1->getView(OCIO::VIEW_SHARED, "sRGB", 0));
+        OCIO_CHECK_ASSERT(config1->viewIsShared("sRGB", "sview1"));
+
+        // Inactive (display, view) pair where the view is a reference to a shared view.
+        OCIO_CHECK_EQUAL(2, config2->getNumViews(OCIO::VIEW_SHARED, "sRGB"));
+        OCIO_CHECK_EQUAL(std::string("view"), config2->getView(OCIO::VIEW_SHARED, "sRGB", 0));
+        OCIO_CHECK_EQUAL(std::string("sview1"), config2->getView(OCIO::VIEW_SHARED, "sRGB", 1));
+        OCIO_CHECK_ASSERT(config2->viewIsShared("sRGB", "sview1"));
+
+        OCIO_CHECK_ASSERT(OCIO::Config::ViewsAreEqual(config1, config2, "sRGB", "sview1"));
+    }
+
+    {
+        // Check that displayHasView method works if (display, view) pair exists regardless of whether the display or view
+        // are active and regardless of whether the view is display-defined or if the view is a reference to a shared view.
+
+        OCIO::ConfigRcPtr cfg1 = config1->createEditableCopy();
+        OCIO_CHECK_ASSERT(cfg1->displayHasView("sRGB", "Raw"   )); // Active display has inactive view (display-defined).
+        OCIO_CHECK_ASSERT(cfg1->displayHasView("sRGB", "view"  )); // Active display has active view (display-defined).
+        OCIO_CHECK_ASSERT(cfg1->displayHasView("sRGB", "sview1")); // Active display has active view (shared).
+        OCIO_CHECK_ASSERT(cfg1->displayHasView("Raw" , "Raw"   )); // Inactive display has inactive view (display-defined).
+
+        OCIO_CHECK_NO_THROW(cfg1->setActiveDisplays("Raw"));
+        OCIO_CHECK_EQUAL(1, cfg1->getNumDisplays());
+        OCIO_CHECK_EQUAL(std::string("Raw"), cfg1->getDefaultDisplay());
+
+        OCIO_CHECK_ASSERT(cfg1->displayHasView("sRGB", "sview1")); // Inactive display has active view (shared).
+
+        OCIO_CHECK_NO_THROW(cfg1->setActiveViews("Raw"));
+        OCIO_REQUIRE_EQUAL(cfg1->getNumViews("sRGB"), 1);
+        OCIO_CHECK_EQUAL(cfg1->getView("sRGB", 0), std::string("Raw"));
+
+        OCIO_CHECK_ASSERT(cfg1->displayHasView("sRGB" , "Raw"  )); // Inactive display has active view (display-defined).
+        OCIO_CHECK_ASSERT(cfg1->displayHasView("sRGB", "sview1")); // Inactive display has inactive view (shared).
+
+        OCIO_CHECK_NO_THROW(cfg1->setActiveDisplays("sRGB"));
+        OCIO_CHECK_EQUAL(1, cfg1->getNumDisplays());
+        OCIO_CHECK_EQUAL(std::string("sRGB"), cfg1->getDefaultDisplay());
+
+        OCIO_CHECK_ASSERT(cfg1->displayHasView("sRGB", "sview1")); // Active display has inactive view (shared).
+    }
+
+    {
+        // Test when a display exists, but a view doesn't exist.
+
+        OCIO::ConfigRcPtr cfg1 = config1->createEditableCopy();
+
+        OCIO_CHECK_EQUAL(std::string("sRGB"), cfg1->getDefaultDisplay());
+        OCIO_CHECK_EQUAL(2, cfg1->getNumViews(OCIO::VIEW_DISPLAY_DEFINED, "sRGB"));
+        OCIO_CHECK_EQUAL(std::string("Raw"), cfg1->getView(OCIO::VIEW_DISPLAY_DEFINED, "sRGB", 0));
+        OCIO_CHECK_EQUAL(std::string("view"), cfg1->getView(OCIO::VIEW_DISPLAY_DEFINED, "sRGB", 1));
+
+        OCIO_CHECK_ASSERT(cfg1->displayHasView("sRGB", "Raw"));
+        OCIO_CHECK_ASSERT(OCIO::Config::ViewsAreEqual(config1, cfg1, "sRGB", "Raw"));
+
+        // Remove the view from the display.
+        OCIO_CHECK_NO_THROW(cfg1->removeDisplayView("sRGB", "Raw"));
+        OCIO_CHECK_EQUAL(1, cfg1->getNumViews(OCIO::VIEW_DISPLAY_DEFINED, "sRGB"));
+        OCIO_CHECK_EQUAL(std::string("view"), cfg1->getView(OCIO::VIEW_DISPLAY_DEFINED, "sRGB", 0));
+
+        OCIO_CHECK_ASSERT(!cfg1->displayHasView("sRGB", "Raw"));
+        OCIO_CHECK_ASSERT(!OCIO::Config::ViewsAreEqual(config1, cfg1, "sRGB", "Raw"));
+    }
+
+    {
+        // Test when a view exists, but a display doesn't exist.
+        
+        OCIO::ConfigRcPtr cfg2 = config2->createEditableCopy();
+
+        OCIO_CHECK_EQUAL(std::string("Raw"), cfg2->getDefaultDisplay());
+        OCIO_REQUIRE_EQUAL(1, cfg2->getNumViews("Raw"));
+        OCIO_REQUIRE_EQUAL(std::string("Raw"), cfg2->getView("Raw", 0));
+        OCIO_CHECK_EQUAL(std::string("Raw"), cfg2->getActiveViews());
+
+        OCIO_CHECK_ASSERT(cfg2->displayHasView("Raw", "Raw"));
+        OCIO_CHECK_ASSERT(OCIO::Config::ViewsAreEqual(config2, cfg2, "Raw", "Raw"));
+
+        // Remove the view from the display and the display itself since it only has no more views.
+        OCIO_REQUIRE_EQUAL(2, cfg2->getNumDisplaysAll());
+        OCIO_CHECK_NO_THROW(cfg2->removeDisplayView("Raw", "Raw"));
+        OCIO_REQUIRE_EQUAL(1, cfg2->getNumDisplaysAll());
+
+        // The view is still active.
+        OCIO_REQUIRE_EQUAL(std::string("Raw"), cfg2->getActiveViews());
+
+        OCIO_CHECK_ASSERT(!cfg2->displayHasView("Raw", "Raw"));
+        OCIO_CHECK_ASSERT(!OCIO::Config::ViewsAreEqual(config2, cfg2, "Raw", "Raw"));
+    }
+
+    {
+        // Test access of config-level shared views for displayHasView method.
+
+        OCIO::ConfigRcPtr cfg1 = config1->createEditableCopy();
+
+        OCIO_CHECK_EQUAL(1, cfg1->getNumViews(OCIO::VIEW_SHARED, "sRGB"));
+        OCIO_CHECK_EQUAL(std::string("sview1"), cfg1->getView(OCIO::VIEW_SHARED, "sRGB", 0));
+        OCIO_CHECK_ASSERT(cfg1->viewIsShared("sRGB", "sview1"));
+
+        OCIO_CHECK_ASSERT(cfg1->displayHasView("sRGB", "sview1"));
+
+        // Remove the shared view from the display.
+        OCIO_CHECK_NO_THROW(cfg1->removeDisplayView("sRGB", "sview1"));
+        OCIO_REQUIRE_EQUAL(std::string("sRGB"), config1->getDefaultDisplay());
+        OCIO_CHECK_EQUAL(0, cfg1->getNumViews(OCIO::VIEW_SHARED, "sRGB"));
+
+        // Shared view still exists in the config.
+        OCIO_CHECK_EQUAL(1, cfg1->getNumViews(OCIO::VIEW_SHARED, nullptr));
+        OCIO_CHECK_EQUAL(std::string("sview1"), cfg1->getView(OCIO::VIEW_SHARED, nullptr, 0));
+        OCIO_CHECK_ASSERT(cfg1->viewIsShared(nullptr, "sview1"));
+
+        OCIO_CHECK_ASSERT(!cfg1->displayHasView("sRGB", "sview1"));
+
+        // When display name is null, displayHasView will only check config level shared views.
+        OCIO_CHECK_ASSERT(cfg1->displayHasView(nullptr, "sview1"));
+    }
+}
+
+OCIO_ADD_TEST(Config, compare_virtual_displays) {
+    static constexpr char CONFIG1[]{ R"(ocio_profile_version: 2
+
+roles:
+  default: raw
+
+file_rules:
+  - !<Rule> {name: Default, colorspace: default}
+
+viewing_rules:
+  - !<Rule> {name: Linear, colorspaces: default}
+
+shared_views:
+  - !<View> {name: Film, view_transform: display_vt, display_colorspace: <USE_DISPLAY_NAME>, looks: look1, rule: Linear, description: Test view}
+  - !<View> {name: view, view_transform: display_vt, display_colorspace: display_cs}
+
+displays:
+  Raw:
+    - !<View> {name: Raw, colorspace: raw}
+  sRGB:
+    - !<View> {name: Raw, colorspace: raw}
+
+virtual_display:
+  - !<View> {name: Raw, colorspace: raw}
+  - !<Views> [Film, view]
+
+looks:
+  - !<Look>
+    name: look1
+    process_space: default
+
+view_transforms:
+  - !<ViewTransform>
+    name: default_vt
+    to_scene_reference: !<CDLTransform> {sat: 1.5}
+
+  - !<ViewTransform>
+    name: display_vt
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+display_colorspaces:
+  - !<ColorSpace>
+    name: display_cs
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+colorspaces:
+  - !<ColorSpace>
+    name: raw
+)" };
+
+    static constexpr char CONFIG2[]{ R"(ocio_profile_version: 2
+
+roles:
+  default: raw
+
+file_rules:
+  - !<Rule> {name: Default, colorspace: default}
+
+viewing_rules:
+  - !<Rule> {name: Linear, colorspaces: default}
+
+shared_views:
+  - !<View> {name: view, view_transform: display_vt, display_colorspace: display_cs}
+
+displays:
+  Raw:
+    - !<View> {name: Raw, colorspace: raw}
+  sRGB:
+    - !<View> {name: Raw, colorspace: raw}
+    - !<Views> [view]
+
+virtual_display:
+  - !<View> {name: Raw, colorspace: raw}
+  - !<View> {name: Film, view_transform: display_vt, display_colorspace: <USE_DISPLAY_NAME>, looks: look1, rule: Linear, description: Test view}
+  - !<Views> [view]
+
+looks:
+  - !<Look>
+    name: look1
+    process_space: default
+
+view_transforms:
+  - !<ViewTransform>
+    name: default_vt
+    to_scene_reference: !<CDLTransform> {sat: 1.5}
+
+  - !<ViewTransform>
+    name: display_vt
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+display_colorspaces:
+  - !<ColorSpace>
+    name: display_cs
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+colorspaces:
+  - !<ColorSpace>
+    name: raw
+)" };
+
+    std::istringstream is;
+    is.str(CONFIG1);
+    OCIO::ConstConfigRcPtr config1, config2;
+    OCIO_CHECK_NO_THROW(config1 = OCIO::Config::CreateFromStream(is));
+    is.clear();
+    is.str(CONFIG2);
+    OCIO_CHECK_NO_THROW(config2 = OCIO::Config::CreateFromStream(is));
+    OCIO_CHECK_NO_THROW(config1->validate());
+    OCIO_CHECK_NO_THROW(config2->validate());
+
+    {
+        // Test that Config::VirtualViewsAreEqual works for a matching virtual view pair across separate configs.
+        // Works regardless of if the virtual view is display-defined in one config and shared in the other.
+
+        // Virtual view is a reference to a shared view.
+        OCIO_REQUIRE_EQUAL(2, config1->getVirtualDisplayNumViews(OCIO::VIEW_SHARED));
+
+        const char * viewName1 = config1->getVirtualDisplayView(OCIO::VIEW_SHARED, 0);
+
+        OCIO_CHECK_EQUAL(std::string("Film"), viewName1);
+        OCIO_CHECK_EQUAL(std::string("display_vt"), config1->getVirtualDisplayViewTransformName(viewName1));
+        OCIO_CHECK_EQUAL(std::string("<USE_DISPLAY_NAME>"), config1->getVirtualDisplayViewColorSpaceName(viewName1));
+        OCIO_CHECK_EQUAL(std::string("look1"), config1->getVirtualDisplayViewLooks(viewName1));
+        OCIO_CHECK_EQUAL(std::string("Linear"), config1->getVirtualDisplayViewRule(viewName1));
+        OCIO_CHECK_EQUAL(std::string("Test view"), config1->getVirtualDisplayViewDescription(viewName1));
+
+        // Virtual view is a reference to a display-defined view.
+        OCIO_REQUIRE_EQUAL(2, config2->getVirtualDisplayNumViews(OCIO::VIEW_DISPLAY_DEFINED));
+
+        const char * viewName2 = config2->getVirtualDisplayView(OCIO::VIEW_DISPLAY_DEFINED, 1);
+
+        OCIO_CHECK_EQUAL(std::string("Film"), viewName2);
+        OCIO_CHECK_EQUAL(std::string("display_vt"), config2->getVirtualDisplayViewTransformName(viewName2));
+        OCIO_CHECK_EQUAL(std::string("<USE_DISPLAY_NAME>"), config2->getVirtualDisplayViewColorSpaceName(viewName2));
+        OCIO_CHECK_EQUAL(std::string("look1"), config2->getVirtualDisplayViewLooks(viewName2));
+        OCIO_CHECK_EQUAL(std::string("Linear"), config2->getVirtualDisplayViewRule(viewName2));
+        OCIO_CHECK_EQUAL(std::string("Test view"), config2->getVirtualDisplayViewDescription(viewName2));
+
+        OCIO_CHECK_EQUAL(std::string(viewName1), std::string(viewName2));
+        OCIO_CHECK_ASSERT(OCIO::Config::VirtualViewsAreEqual(config1, config2, viewName1));
+    }
+    {
+        // Virtual views are both display-defined.
+        OCIO_REQUIRE_EQUAL(1, config1->getVirtualDisplayNumViews(OCIO::VIEW_DISPLAY_DEFINED));
+
+        const char * viewName1 = config1->getVirtualDisplayView(OCIO::VIEW_DISPLAY_DEFINED, 0);
+
+        OCIO_CHECK_EQUAL(std::string("Raw"), viewName1);
+        OCIO_CHECK_EQUAL(std::string(""), config1->getVirtualDisplayViewTransformName(viewName1));
+        OCIO_CHECK_EQUAL(std::string("raw"), config1->getVirtualDisplayViewColorSpaceName(viewName1));
+        OCIO_CHECK_EQUAL(std::string(""), config1->getVirtualDisplayViewLooks(viewName1));
+        OCIO_CHECK_EQUAL(std::string(""), config1->getVirtualDisplayViewRule(viewName1));
+        OCIO_CHECK_EQUAL(std::string(""), config1->getVirtualDisplayViewDescription(viewName1));
+
+        const char * viewName2 = config2->getVirtualDisplayView(OCIO::VIEW_DISPLAY_DEFINED, 0);
+
+        OCIO_CHECK_EQUAL(std::string("Raw"), viewName2);
+        OCIO_CHECK_EQUAL(std::string(""), config2->getVirtualDisplayViewTransformName(viewName2));
+        OCIO_CHECK_EQUAL(std::string("raw"), config2->getVirtualDisplayViewColorSpaceName(viewName2));
+        OCIO_CHECK_EQUAL(std::string(""), config2->getVirtualDisplayViewLooks(viewName2));
+        OCIO_CHECK_EQUAL(std::string(""), config2->getVirtualDisplayViewRule(viewName2));
+        OCIO_CHECK_EQUAL(std::string(""), config2->getVirtualDisplayViewDescription(viewName2));
+
+        OCIO_CHECK_EQUAL(std::string(viewName1), std::string(viewName2));
+        OCIO_CHECK_ASSERT(OCIO::Config::VirtualViewsAreEqual(config1, config2, viewName1));
+    }
+    {
+        // Virtual views are both shared.
+        const char * viewName1 = config1->getVirtualDisplayView(OCIO::VIEW_SHARED, 1);
+
+        OCIO_CHECK_EQUAL(std::string("view"), viewName1);
+        OCIO_CHECK_EQUAL(std::string("display_vt"), config1->getVirtualDisplayViewTransformName(viewName1));
+        OCIO_CHECK_EQUAL(std::string("display_cs"), config1->getVirtualDisplayViewColorSpaceName(viewName1));
+        OCIO_CHECK_EQUAL(std::string(""), config1->getVirtualDisplayViewLooks(viewName1));
+        OCIO_CHECK_EQUAL(std::string(""), config1->getVirtualDisplayViewRule(viewName1));
+        OCIO_CHECK_EQUAL(std::string(""), config1->getVirtualDisplayViewDescription(viewName1));
+
+        OCIO_REQUIRE_EQUAL(1, config2->getVirtualDisplayNumViews(OCIO::VIEW_SHARED));
+
+        const char * viewName2 = config2->getVirtualDisplayView(OCIO::VIEW_SHARED, 0);
+
+        OCIO_CHECK_EQUAL(std::string("view"), viewName2);
+        OCIO_CHECK_EQUAL(std::string("display_vt"), config2->getVirtualDisplayViewTransformName(viewName2));
+        OCIO_CHECK_EQUAL(std::string("display_cs"), config2->getVirtualDisplayViewColorSpaceName(viewName2));
+        OCIO_CHECK_EQUAL(std::string(""), config2->getVirtualDisplayViewLooks(viewName2));
+        OCIO_CHECK_EQUAL(std::string(""), config2->getVirtualDisplayViewRule(viewName2));
+        OCIO_CHECK_EQUAL(std::string(""), config2->getVirtualDisplayViewDescription(viewName2));
+
+        OCIO_CHECK_EQUAL(std::string(viewName1), std::string(viewName2));
+        OCIO_CHECK_ASSERT(OCIO::Config::VirtualViewsAreEqual(config1, config2, viewName1));
+
+        OCIO_CHECK_EQUAL(std::string(viewName1), std::string(viewName2));
+        OCIO_CHECK_ASSERT(OCIO::Config::VirtualViewsAreEqual(config1, config2, viewName1));
+    }
+    {
+        // Test when a shared virtual view exists in one config but not the other.
+        OCIO::ConfigRcPtr cfg = config1->createEditableCopy();
+
+        OCIO_CHECK_ASSERT(config1->hasVirtualView("Film"));
+        OCIO_CHECK_ASSERT(config1->virtualViewIsShared("Film"));
+
+        OCIO_REQUIRE_EQUAL(2, cfg->getVirtualDisplayNumViews(OCIO::VIEW_SHARED));
+        OCIO_CHECK_ASSERT(cfg->hasVirtualView("Film"));
+        OCIO_CHECK_ASSERT(cfg->virtualViewIsShared("Film"));
+
+        OCIO_CHECK_ASSERT(OCIO::Config::VirtualViewsAreEqual(config1, cfg, "Film"));
+
+        // Check against another config where the virtual view is display-defined.
+        OCIO_CHECK_ASSERT(OCIO::Config::VirtualViewsAreEqual(config2, cfg, "Film"));
+
+        // Remove a shared view from the virtual display.
+        cfg->removeVirtualDisplayView("Film");
+
+        OCIO_REQUIRE_EQUAL(1, cfg->getVirtualDisplayNumViews(OCIO::VIEW_SHARED));
+        OCIO_CHECK_ASSERT(!cfg->hasVirtualView("Film"));
+        OCIO_CHECK_ASSERT(!cfg->virtualViewIsShared("Film"));
+
+        OCIO_CHECK_ASSERT(!OCIO::Config::VirtualViewsAreEqual(config1, cfg, "Film"));        
+        OCIO_CHECK_ASSERT(!OCIO::Config::VirtualViewsAreEqual(config2, cfg, "Film"));
+    }
+    {
+        // Test when a display-defined virtual view exists in one config but not the other.
+        OCIO::ConfigRcPtr cfg = config2->createEditableCopy();
+
+        // Remove a display-defined view from the virtual display.
+        OCIO_CHECK_ASSERT(config2->hasVirtualView("Film"));
+        OCIO_CHECK_ASSERT(!config2->virtualViewIsShared("Film"));   // Confirm display-defined
+
+        OCIO_REQUIRE_EQUAL(2, cfg->getVirtualDisplayNumViews(OCIO::VIEW_DISPLAY_DEFINED));
+        OCIO_CHECK_ASSERT(cfg->hasVirtualView("Film"));
+        OCIO_CHECK_ASSERT(!cfg->virtualViewIsShared("Film"));       // Confirm display-defined
+
+        OCIO_CHECK_ASSERT(OCIO::Config::VirtualViewsAreEqual(config2, cfg, "Film"));
+
+        // Check against another config where the virtual view is a reference to a shared view.
+        OCIO_CHECK_ASSERT(OCIO::Config::VirtualViewsAreEqual(config1, cfg, "Film"));
+
+        // Remove a display-defined view from the virtual display.
+        cfg->removeVirtualDisplayView("Film");
+
+        OCIO_REQUIRE_EQUAL(1, cfg->getVirtualDisplayNumViews(OCIO::VIEW_DISPLAY_DEFINED));
+        OCIO_CHECK_ASSERT(!cfg->hasVirtualView("Film"));
+        
+        OCIO_CHECK_ASSERT(!OCIO::Config::VirtualViewsAreEqual(config2, cfg, "Film"));        
+        OCIO_CHECK_ASSERT(!OCIO::Config::VirtualViewsAreEqual(config1, cfg, "Film"));
+    }
 }
diff --git a/tests/python/ConfigTest.py b/tests/python/ConfigTest.py
index 5130ebbf88..f63ad15af6 100644
--- a/tests/python/ConfigTest.py
+++ b/tests/python/ConfigTest.py
@@ -288,7 +288,7 @@ def test_copy(self):
         self.assertEqual(other.getFileRules().getNumEntries(), cfg.getFileRules().getNumEntries() - 1)
 
     def test_shared_views(self):
-        # Test these Config functions: addSharedView, getSharedViews, removeSharedView.
+        # Test these Config functions: addSharedView, getSharedViews, removeSharedView, clearSharedViews.
 
         cfg = OCIO.Config.CreateRaw()
         views = cfg.getSharedViews()
@@ -413,11 +413,7 @@ def test_shared_views(self):
         cfg.removeSharedView('view3')
         views = cfg.getSharedViews()
         self.assertEqual(5, len(views))
-        cfg.removeSharedView('view4')
-        cfg.removeSharedView('view5')
-        cfg.removeSharedView('view6')
-        cfg.removeSharedView('view1')
-        cfg.removeSharedView('view2')
+        cfg.clearSharedViews()
         views = cfg.getSharedViews()
         self.assertEqual(0, len(views))
 
@@ -842,12 +838,35 @@ def test_virtual_display(self):
                                              "sRGB")))
         self.assertEqual(1, len(cfg.getViews(OCIO.VIEW_SHARED, "sRGB")))
 
+        self.assertTrue(cfg.displayHasView("sRGB", "sview1"))
+        self.assertFalse(cfg.displayHasView("sRGB", "sview2"))
+        self.assertFalse(cfg.displayHasView("sRGB", " "))
+
+        self.assertTrue(cfg.viewIsShared("sRGB", "sview1"))
+        self.assertFalse(cfg.viewIsShared("sRGB", "Raw"))
+        self.assertFalse(cfg.viewIsShared("sRGB", " "))
+
+        other = copy.deepcopy(cfg)
+
+        self.assertTrue(OCIO.Config.ViewsAreEqual(cfg, other, "sRGB", "sview1"))
+        self.assertTrue(OCIO.Config.ViewsAreEqual(cfg, other, "sRGB", "Raw"))
+        self.assertTrue(OCIO.Config.ViewsAreEqual(cfg, other, "sRGB", "view"))
+
+        cfg.removeDisplayView("sRGB", "sview1")
+        cfg.removeDisplayView("sRGB", "Raw")
+        cfg.removeDisplayView("sRGB", "view")
+
+        self.assertFalse(OCIO.Config.ViewsAreEqual(cfg, other, "sRGB", "sview1"))
+        self.assertFalse(OCIO.Config.ViewsAreEqual(cfg, other, "sRGB", "Raw"))
+        self.assertFalse(OCIO.Config.ViewsAreEqual(cfg, other, "sRGB", "view"))
+
         # Validate the virtual display information
         self.assertEqual(
             2,
             len(cfg.getVirtualDisplayViews(OCIO.VIEW_DISPLAY_DEFINED)))
 
         view_name = cfg.getVirtualDisplayViews(OCIO.VIEW_DISPLAY_DEFINED)[0]
+        self.assertTrue(cfg.hasVirtualView(view_name))
         self.assertEqual("Raw", view_name)
         self.assertEqual("", cfg.getVirtualDisplayViewTransformName(view_name))
         self.assertEqual("raw",
@@ -856,7 +875,10 @@ def test_virtual_display(self):
         self.assertEqual("", cfg.getVirtualDisplayViewRule(view_name))
         self.assertEqual("", cfg.getVirtualDisplayViewDescription(view_name))
 
+        self.assertTrue(OCIO.Config.VirtualViewsAreEqual(cfg, other, view_name))
+
         view_name = cfg.getVirtualDisplayViews(OCIO.VIEW_DISPLAY_DEFINED)[1]
+        self.assertTrue(cfg.hasVirtualView(view_name))
         self.assertEqual("Film", view_name)
         self.assertEqual("display_vt",
                          cfg.getVirtualDisplayViewTransformName(view_name))
@@ -866,13 +888,39 @@ def test_virtual_display(self):
         self.assertEqual("", cfg.getVirtualDisplayViewRule(view_name))
         self.assertEqual("", cfg.getVirtualDisplayViewDescription(view_name))
 
+        self.assertTrue(OCIO.Config.VirtualViewsAreEqual(cfg, other, view_name))
+
         self.assertEqual(1, len(cfg.getVirtualDisplayViews(OCIO.VIEW_SHARED)))
         self.assertEqual("sview2",
                          cfg.getVirtualDisplayViews(OCIO.VIEW_SHARED)[0])
+        
+        self.assertTrue(cfg.hasVirtualView("sview2"))
+        self.assertTrue(cfg.virtualViewIsShared("sview2"))
+        self.assertTrue(OCIO.Config.VirtualViewsAreEqual(cfg, other, "sview2"))
+
+        other.addVirtualDisplayView("sview3", "", "raw")
+
+        self.assertFalse(cfg.hasVirtualView("sview3"))
+        self.assertTrue(other.hasVirtualView("sview3"))
+        self.assertFalse(OCIO.Config.VirtualViewsAreEqual(cfg, other, "sview3"))
+
+        # Test a virtual view that doesn't exist in either
+        self.assertFalse(cfg.hasVirtualView(" "))
+        self.assertFalse(other.hasVirtualView(" "))
+        self.assertFalse(OCIO.Config.VirtualViewsAreEqual(cfg, other, " "))
+
+        self.assertFalse(cfg.hasVirtualView("nonexistent"))
+        self.assertFalse(other.hasVirtualView("nonexistent"))
+        self.assertFalse(OCIO.Config.VirtualViewsAreEqual(cfg, other, "nonexistent"))
 
         # Remove a view from the virtual display
         cfg.removeVirtualDisplayView("Raw")
 
+        self.assertFalse(cfg.hasVirtualView("Raw"))
+        self.assertTrue(other.hasVirtualView("Raw"))
+
+        self.assertFalse(OCIO.Config.VirtualViewsAreEqual(cfg, other, "Raw"))
+
         self.assertEqual(
             1,
             len(cfg.getVirtualDisplayViews(OCIO.VIEW_DISPLAY_DEFINED)))
@@ -891,6 +939,14 @@ def test_virtual_display(self):
             len(cfg.getVirtualDisplayViews(OCIO.VIEW_DISPLAY_DEFINED)))
         self.assertEqual(0, len(cfg.getVirtualDisplayViews(OCIO.VIEW_SHARED)))
 
+        self.assertFalse(cfg.hasVirtualView("sview2"))
+        self.assertFalse(cfg.virtualViewIsShared("sview2"))
+
+        self.assertTrue(other.hasVirtualView("sview2"))
+        self.assertTrue(other.virtualViewIsShared("sview2"))
+
+        self.assertFalse(OCIO.Config.VirtualViewsAreEqual(cfg, other, "sview2"))
+
         cfg.addVirtualDisplaySharedView("sview2")
         self.assertEqual(
             1,


Reviews:
user: login: doug-walker
body: Thanks Annie, this is excellent work!

I made a few suggestions below. If you could look at the comments I suggested for OpenColorIO.h and check that there is a test for each aspect of the documented functionality for each new function, that would be appreciated.

I notice that one of the Linux CI runs is failing due to a frozen Python docs problem. Currently the frozen docs system is broken and so we will handle that separately. As long as the Windows and Mac tests are passing, that's all you need to be concerned with for now.
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2024-10-18T03:45:24Z
user: login: annieln
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-10-18T17:06:02Z
user: login: annieln
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2024-10-18T17:11:59Z
user: login: doug-walker
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2024-10-18T18:35:48Z
user: login: doug-walker
body: @annieln, apologies for the delay in getting this reviewed!

I still need to review your unit tests, but there is one addition below I'd like you to make. When you push the commit, we will make sure the CI runs.
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-15T19:41:43Z
user: login: doug-walker
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-15T21:34:34Z
user: login: doug-walker
body: @annieln, here is the rest of my review. I have a few more requests, but you've done really great work on this!  Big thanks for all the work on the unit tests, we love to see this!
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-15T22:43:30Z
user: login: doug-walker
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-17T01:26:30Z
user: login: doug-walker
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-17T01:29:18Z
user: login: annieln
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-17T10:25:27Z
user: login: doug-walker
body: Great work @annieln!!

Apologies for the delay getting it reviewed and thank you for being so diligent in addressing the comments. This is one of the biggest contributions we've received in the history of our Dev Days! These new capabilities will be very useful to the community!
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-17T16:50:56Z
user: login: cozdas
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T04:30:34Z
user: login: cozdas
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T04:31:07Z
user: login: cozdas
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T04:33:32Z
user: login: cozdas
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T04:34:29Z
user: login: cozdas
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T04:35:00Z
user: login: cozdas
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T04:42:19Z
user: login: cozdas
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T04:51:23Z
user: login: cozdas
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-20T04:57:05Z
user: login: doug-walker
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T03:30:53Z
user: login: cozdas
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T05:16:18Z
user: login: cozdas
body: Approved.
Thanks for the great work.
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-26T03:46:39Z

Review comments:
diff_hunk: @@ -883,6 +888,11 @@ class OCIOEXPORT Config
     int getNumViews(const char * display, const char * colorspaceName) const;
     const char * getView(const char * display, const char * colorspaceName, int index) const;
 
+    static bool viewsAreEqual(const ConstConfigRcPtr & first,
+                              const ConstConfigRcPtr & second,
+                              const char * dispName,
+                              const char * viewName);
path: include/OpenColorIO/OpenColorIO.h
user: login: doug-walker
body: Please use "ViewsAreEqual" since the names of all static methods are capitalized. Same for "VirtualViewsAreEqual".
created_at: 2024-10-18T02:47:46Z
author_association: COLLABORATOR
diff_hunk: @@ -852,13 +852,18 @@ class OCIOEXPORT Config
      * \ref Config::validate will throw if the config does not contain
      * the matching display color space.
      */
+
+    bool viewIsShared(const char * dispName, const char * viewName) const;
+
     /// Will throw if view or colorSpaceName are null or empty.
     void addSharedView(const char * view, const char * viewTransformName,
                        const char * colorSpaceName, const char * looks,
                        const char * ruleName, const char * description);
     /// Remove a shared view.  Will throw if the view does not exist.
     void removeSharedView(const char * view);
 
path: include/OpenColorIO/OpenColorIO.h
user: login: doug-walker
body: /// Clear all shared views. This will throw if any displays are still using the shared views.
created_at: 2024-10-18T03:03:31Z
author_association: COLLABORATOR
diff_hunk: @@ -883,6 +888,11 @@ class OCIOEXPORT Config
     int getNumViews(const char * display, const char * colorspaceName) const;
     const char * getView(const char * display, const char * colorspaceName, int index) const;
 
path: include/OpenColorIO/OpenColorIO.h
user: login: doug-walker
body: ```
/**
 * \brief Compare views in a pair of configs.
 *
 * Will return false if either of the views does not exist. This will return true even
 * if the view is display-defined in one config and a reference to a shared view in the
 * other config (both within the same display), as long as the contents match. The
 * description text (if any) is ignored, since it is not a functional difference.
 */
```
created_at: 2024-10-18T03:12:55Z
author_association: COLLABORATOR
diff_hunk: @@ -900,6 +910,8 @@ class OCIOEXPORT Config
     /// Returns the description attribute of a (display, view) pair.
     const char * getDisplayViewDescription(const char * display, const char * view) const noexcept;
 
path: include/OpenColorIO/OpenColorIO.h
user: login: doug-walker
body: ```
/**
 * \brief Determine if a display and view exist.
 *
 * This returns false if either the display or view doesn't exist. It works regardless
 * of whether the display or view are active, and it works regardless of whether the
 * view is display-defined or if the display has this as a shared view. It will only
 * check config-level shared views if dispName is null. It will not check config level
 * shared views if dispName is not null.
 */

```
created_at: 2024-10-18T03:19:26Z
author_association: COLLABORATOR
diff_hunk: @@ -3812,6 +3944,13 @@ const char * Config::getVirtualDisplayViewColorSpaceName(const char * view) cons
 {
     if (!view) return "";
 
+    // TODO: Remove the following work-around once bug for shared views is fixed.
path: src/OpenColorIO/Config.cpp
user: login: doug-walker
body: Actually, despite what I wrote, you could remove the TODO, I think your fix is sufficient.
created_at: 2024-10-18T03:23:31Z
author_association: COLLABORATOR
diff_hunk: @@ -963,6 +975,9 @@ class OCIOEXPORT Config
      *
      */
 
path: include/OpenColorIO/OpenColorIO.h
user: login: doug-walker
body: Please add comments for the virtual case, similar to what I wrote above.
created_at: 2024-10-18T03:24:10Z
author_association: COLLABORATOR
diff_hunk: @@ -7338,7 +7349,10 @@ OCIO_ADD_TEST(Config, add_remove_display)
 
     // Add a (display, view) pair.
 
+    // todo (annie): just a note here that this config will have two (display, view) pairs): sRGB and disp1
path: tests/cpu/Config_tests.cpp
user: login: doug-walker
body: You could remove the todo and make this a comment if you feel it clarifies the test.
created_at: 2024-10-18T03:30:24Z
author_association: COLLABORATOR
diff_hunk: @@ -413,11 +413,12 @@ def test_shared_views(self):
         cfg.removeSharedView('view3')
         views = cfg.getSharedViews()
         self.assertEqual(5, len(views))
-        cfg.removeSharedView('view4')
-        cfg.removeSharedView('view5')
-        cfg.removeSharedView('view6')
-        cfg.removeSharedView('view1')
-        cfg.removeSharedView('view2')
+        cfg.clearSharedViews()
+        # cfg.removeSharedView('view4')
+        # cfg.removeSharedView('view5')
+        # cfg.removeSharedView('view6')
+        # cfg.removeSharedView('view1')
+        # cfg.removeSharedView('view2')
path: tests/python/ConfigTest.py
user: login: doug-walker
body: Remove commented code.
created_at: 2024-10-18T03:38:21Z
author_association: COLLABORATOR
diff_hunk: @@ -7338,7 +7349,10 @@ OCIO_ADD_TEST(Config, add_remove_display)
 
     // Add a (display, view) pair.
 
+    // todo (annie): just a note here that this config will have two (display, view) pairs): sRGB and disp1
path: tests/cpu/Config_tests.cpp
user: login: annieln
body: Ah, that was a note I forgot to remove to help me understand what was going on in that test. Will be removing!
created_at: 2024-10-18T17:06:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -852,13 +852,18 @@ class OCIOEXPORT Config
      * \ref Config::validate will throw if the config does not contain
      * the matching display color space.
      */
+
+    bool viewIsShared(const char * dispName, const char * viewName) const;
+
     /// Will throw if view or colorSpaceName are null or empty.
     void addSharedView(const char * view, const char * viewTransformName,
                        const char * colorSpaceName, const char * looks,
                        const char * ruleName, const char * description);
     /// Remove a shared view.  Will throw if the view does not exist.
     void removeSharedView(const char * view);
 
path: include/OpenColorIO/OpenColorIO.h
user: login: annieln
body: Sorry, I'm just checking - is this meant for clearSharedViews instead of viewIsShared? 
created_at: 2024-10-18T17:11:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -852,13 +852,18 @@ class OCIOEXPORT Config
      * \ref Config::validate will throw if the config does not contain
      * the matching display color space.
      */
+
+    bool viewIsShared(const char * dispName, const char * viewName) const;
+
     /// Will throw if view or colorSpaceName are null or empty.
     void addSharedView(const char * view, const char * viewTransformName,
                        const char * colorSpaceName, const char * looks,
                        const char * ruleName, const char * description);
     /// Remove a shared view.  Will throw if the view does not exist.
     void removeSharedView(const char * view);
 
path: include/OpenColorIO/OpenColorIO.h
user: login: doug-walker
body: Yes, sorry, I put the comment on the wrong line.
created_at: 2024-10-18T18:35:48Z
author_association: COLLABORATOR
diff_hunk: @@ -3812,6 +3948,12 @@ const char * Config::getVirtualDisplayViewColorSpaceName(const char * view) cons
 {
     if (!view) return "";
 
+    // Get the colorspace name for the case where a virtual view is shared.
+    if (virtualViewIsShared(view))
+    {
+        return getDisplayViewColorSpaceName(nullptr, view);
+    }
+
path: src/OpenColorIO/Config.cpp
user: login: doug-walker
body: Please add a similar if-statement to getVirtualDisplayViewLooks, getVirtualDisplayViewRule, getVirtualDisplayViewDescription, and removeVirtualDisplayView.
created_at: 2025-05-15T18:02:52Z
author_association: COLLABORATOR
diff_hunk: @@ -3812,6 +3948,12 @@ const char * Config::getVirtualDisplayViewColorSpaceName(const char * view) cons
 {
     if (!view) return "";
 
+    // Get the colorspace name for the case where a virtual view is shared.
+    if (virtualViewIsShared(view))
+    {
+        return getDisplayViewColorSpaceName(nullptr, view);
+    }
+
path: src/OpenColorIO/Config.cpp
user: login: doug-walker
body: Actually, removeVirtualDisplayView already handles the shared view case, so that one is ok as is.
created_at: 2025-05-15T21:34:34Z
author_association: COLLABORATOR
diff_hunk: @@ -8489,38 +8767,65 @@ active_views: []
     OCIO_CHECK_EQUAL(2, config->getNumViews(OCIO::VIEW_DISPLAY_DEFINED, "sRGB"));
     OCIO_CHECK_EQUAL(1, config->getNumViews(OCIO::VIEW_SHARED, "sRGB"));
 
+    OCIO_CHECK_EQUAL(std::string("sview1"), config->getView(OCIO::VIEW_SHARED, "sRGB", 0));
+    OCIO_CHECK_ASSERT(config->displayHasView("sRGB", "sview1"));
+    OCIO_CHECK_ASSERT(config->viewIsShared("sRGB", "sview1"));
+
+    OCIO_CHECK_EQUAL(std::string("raw"), config->getDisplayViewColorSpaceName("sRGB", "sview1"));
+    OCIO_CHECK_EQUAL(std::string("raw"), config->getVirtualDisplayViewColorSpaceName("sview2"));
+
     // Step 3 - Validate the virtual display information.
 
     {
         OCIO::ConfigRcPtr cfg = config->createEditableCopy();
 
+        OCIO_CHECK_ASSERT(OCIO::Config::ViewsAreEqual(config, cfg, "sRGB", "sview1"));
+        OCIO_CHECK_ASSERT(OCIO::Config::ViewsAreEqual(config, cfg, "sRGB", "Raw"));
+        OCIO_CHECK_ASSERT(OCIO::Config::ViewsAreEqual(config, cfg, "sRGB", "view"));
+
         OCIO_REQUIRE_EQUAL(2, cfg->getVirtualDisplayNumViews(OCIO::VIEW_DISPLAY_DEFINED));
 
         const char * viewName = cfg->getVirtualDisplayView(OCIO::VIEW_DISPLAY_DEFINED, 0);
 
+        OCIO_CHECK_ASSERT(cfg->hasVirtualView(viewName));
+
         OCIO_CHECK_EQUAL(std::string("Raw"), viewName);
         OCIO_CHECK_EQUAL(std::string(""), cfg->getVirtualDisplayViewTransformName(viewName));
         OCIO_CHECK_EQUAL(std::string("raw"), cfg->getVirtualDisplayViewColorSpaceName(viewName));
         OCIO_CHECK_EQUAL(std::string(""), cfg->getVirtualDisplayViewLooks(viewName));
         OCIO_CHECK_EQUAL(std::string(""), cfg->getVirtualDisplayViewRule(viewName));
         OCIO_CHECK_EQUAL(std::string(""), cfg->getVirtualDisplayViewDescription(viewName));
 
+        OCIO_CHECK_ASSERT(OCIO::Config::VirtualViewsAreEqual(config, cfg, viewName));
+
         viewName = cfg->getVirtualDisplayView(OCIO::VIEW_DISPLAY_DEFINED, 1);
 
-        OCIO_CHECK_EQUAL(std::string("Film"), cfg->getVirtualDisplayView(OCIO::VIEW_DISPLAY_DEFINED, 1));
+        OCIO_CHECK_ASSERT(cfg->hasVirtualView(viewName));
path: tests/cpu/Config_tests.cpp
user: login: doug-walker
body: You already checked this on line 8790.  Likewise, lines 8799 and 8812 seem to be redundant.
created_at: 2025-05-15T21:58:18Z
author_association: COLLABORATOR
diff_hunk: @@ -8933,6 +9243,238 @@ OCIO_ADD_TEST(Config, virtual_display_exceptions)
                           " which is not defined.");
 }
 
+OCIO_ADD_TEST(Config, compare_virtual_displays) {
+    static constexpr char CONFIG1[]{ R"(ocio_profile_version: 2
+
+roles:
+  default: raw
+
+file_rules:
+  - !<Rule> {name: Default, colorspace: default}
+
+shared_views:
+  - !<View> {name: Film, view_transform: display_vt, display_colorspace: <USE_DISPLAY_NAME>}
+  - !<View> {name: view, view_transform: display_vt, display_colorspace: display_cs}
+
+displays:
+  Raw:
+    - !<View> {name: Raw, colorspace: raw}
+  sRGB:
+    - !<View> {name: Raw, colorspace: raw}
+
+virtual_display:
+  - !<View> {name: Raw, colorspace: raw}
+  - !<Views> [Film, view]
+
+view_transforms:
+  - !<ViewTransform>
+    name: default_vt
+    to_scene_reference: !<CDLTransform> {sat: 1.5}
+
+  - !<ViewTransform>
+    name: display_vt
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+display_colorspaces:
+  - !<ColorSpace>
+    name: display_cs
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+colorspaces:
+  - !<ColorSpace>
+    name: raw
+)" };
+
+    static constexpr char CONFIG2[]{ R"(ocio_profile_version: 2
+
+roles:
+  default: raw
+
+file_rules:
+  - !<Rule> {name: Default, colorspace: default}
+
+shared_views:
+  - !<View> {name: view, view_transform: display_vt, display_colorspace: display_cs}
+
+displays:
+  Raw:
+    - !<View> {name: Raw, colorspace: raw}
+  sRGB:
+    - !<View> {name: Raw, colorspace: raw}
+    - !<Views> [view]
+
+virtual_display:
+  - !<View> {name: Raw, colorspace: raw}
+  - !<View> {name: Film, view_transform: display_vt, display_colorspace: <USE_DISPLAY_NAME>}
+  - !<Views> [view]
+
+view_transforms:
+  - !<ViewTransform>
+    name: default_vt
+    to_scene_reference: !<CDLTransform> {sat: 1.5}
+
+  - !<ViewTransform>
+    name: display_vt
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+display_colorspaces:
+  - !<ColorSpace>
+    name: display_cs
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+colorspaces:
+  - !<ColorSpace>
+    name: raw
+)" };
+
+    std::istringstream is;
+    is.str(CONFIG1);
+    OCIO::ConstConfigRcPtr config1, config2;
+    OCIO_CHECK_NO_THROW(config1 = OCIO::Config::CreateFromStream(is));
+    is.clear();
+    is.str(CONFIG2);
+    OCIO_CHECK_NO_THROW(config2 = OCIO::Config::CreateFromStream(is));
+    OCIO_CHECK_NO_THROW(config1->validate());
+    OCIO_CHECK_NO_THROW(config2->validate());
+
+    {
+        // Test that Config::VirtualViewsAreEqual works for a matching virtual view pair across separate configs.
+        // Works regardless of if the virtual view is display-defined in one config and shared in the other.
+
+        // Virtual view is a reference to a shared view.
+        OCIO_REQUIRE_EQUAL(2, config1->getVirtualDisplayNumViews(OCIO::VIEW_SHARED));
+
+        const char * viewName1 = config1->getVirtualDisplayView(OCIO::VIEW_SHARED, 0);
+
+        OCIO_CHECK_EQUAL(std::string("Film"), viewName1);
+        OCIO_CHECK_EQUAL(std::string("display_vt"), config1->getVirtualDisplayViewTransformName(viewName1));
+        OCIO_CHECK_EQUAL(std::string("<USE_DISPLAY_NAME>"), config1->getVirtualDisplayViewColorSpaceName(viewName1));
+        OCIO_CHECK_EQUAL(std::string(""), config1->getVirtualDisplayViewLooks(viewName1));
+        OCIO_CHECK_EQUAL(std::string(""), config1->getVirtualDisplayViewRule(viewName1));
+        OCIO_CHECK_EQUAL(std::string(""), config1->getVirtualDisplayViewDescription(viewName1));
path: tests/cpu/Config_tests.cpp
user: login: doug-walker
body: Looks, rule, and description are actually broken, but the test is not picking it up. Please change the config to use this:
`  - !<View> {name: Film, view_transform: display_vt, display_colorspace: <USE_DISPLAY_NAME>, looks: look1, rule: Linear, description: Test view}`

You may need to add something like this for the config to load:
```
looks:
  - !<Look>
    name: look1
```
created_at: 2025-05-15T22:11:17Z
author_association: COLLABORATOR
diff_hunk: @@ -3749,6 +3749,271 @@ OCIO_ADD_TEST(Config, display_view_order)
     OCIO_CHECK_EQUAL(std::string(config->getView("sRGB_B", 1)), "View_1");
 }
 
+OCIO_ADD_TEST(Config, compare_displays) {
+    static constexpr char CONFIG1[]{ R"(ocio_profile_version: 2
+
+roles:
+  default: raw
+
+file_rules:
+  - !<Rule> {name: Default, colorspace: default}
+
+shared_views:
+  - !<View> {name: sview1, colorspace: raw}
+
+displays:
+  Raw:
+    - !<View> {name: Raw, colorspace: raw}
+  sRGB:
+    - !<View> {name: Raw, colorspace: raw}
+    - !<View> {name: view, view_transform: display_vt, display_colorspace: display_cs}
+    - !<Views> [sview1]
+
+active_displays: [sRGB]
+active_views: [view, sview1]
+
+view_transforms:
+  - !<ViewTransform>
+    name: default_vt
+    to_scene_reference: !<CDLTransform> {sat: 1.5}
+
+  - !<ViewTransform>
+    name: display_vt
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+display_colorspaces:
+  - !<ColorSpace>
+    name: display_cs
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+colorspaces:
+  - !<ColorSpace>
+    name: raw
+)" };
+
+    static constexpr char CONFIG2[]{ R"(ocio_profile_version: 2
+
+roles:
+  default: raw
+
+file_rules:
+  - !<Rule> {name: Default, colorspace: default}
+
+shared_views:
+  - !<View> {name: view, view_transform: display_vt, display_colorspace: display_cs}
+  - !<View> {name: sview1, colorspace: raw}
+
+displays:
+  Raw:
+    - !<View> {name: Raw, colorspace: raw}
+  sRGB:
+    - !<View> {name: Raw, colorspace: raw}
+    - !<Views> [view, sview1]
+
+active_displays: [Raw]
+active_views: [Raw]
+
+view_transforms:
+  - !<ViewTransform>
+    name: default_vt
+    to_scene_reference: !<CDLTransform> {sat: 1.5}
+
+  - !<ViewTransform>
+    name: display_vt
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+display_colorspaces:
+  - !<ColorSpace>
+    name: display_cs
+    to_display_reference: !<CDLTransform> {sat: 1.5}
+
+colorspaces:
+  - !<ColorSpace>
+    name: raw
+)" };
+
path: tests/cpu/Config_tests.cpp
user: login: doug-walker
body: Please add a few usages of viewIsShared somewhere in this test, returning both true and false.
created_at: 2025-05-15T22:21:24Z
author_association: COLLABORATOR
diff_hunk: @@ -8933,6 +9243,238 @@ OCIO_ADD_TEST(Config, virtual_display_exceptions)
                           " which is not defined.");
 }
 
path: tests/cpu/Config_tests.cpp
user: login: doug-walker
body: Please move this test to tests/cpu/Display_tests.cpp.
created_at: 2025-05-15T22:28:50Z
author_association: COLLABORATOR
diff_hunk: @@ -3749,6 +3749,271 @@ OCIO_ADD_TEST(Config, display_view_order)
     OCIO_CHECK_EQUAL(std::string(config->getView("sRGB_B", 1)), "View_1");
 }
 
path: tests/cpu/Config_tests.cpp
user: login: doug-walker
body: Please move this test to tests/cpu/Display_tests.cpp.

And please add a test of clearSharedViews to the "basic" test in that module.
created_at: 2025-05-15T22:30:08Z
author_association: COLLABORATOR
diff_hunk: @@ -842,12 +838,22 @@ def test_virtual_display(self):
                                              "sRGB")))
         self.assertEqual(1, len(cfg.getViews(OCIO.VIEW_SHARED, "sRGB")))
 
+        self.assertTrue(cfg.displayHasView("sRGB", "sview1"))
+        self.assertTrue(cfg.viewIsShared("sRGB", "sview1"))
path: tests/python/ConfigTest.py
user: login: doug-walker
body: For all the functions that return booleans, please have a test where it returns true, another where it returns false, and a third where the arguments are bogus, e.g. a name that's either empty or not in the config.
created_at: 2025-05-15T22:34:59Z
author_association: COLLABORATOR
diff_hunk: @@ -883,6 +890,19 @@ class OCIOEXPORT Config
     int getNumViews(const char * display, const char * colorspaceName) const;
     const char * getView(const char * display, const char * colorspaceName, int index) const;
 
+    /**
+     * \brief Compare views in a pair of configs.
+     *
+     * Will return false if either of the views does not exist. This will return true even
+     * if the view is display-defined in one config and a reference to a shared view in the
+     * other config (both within the same display), as long as the contents match. The
+     * description text (if any) is ignored, since it is not a functional difference.
+     */
path: include/OpenColorIO/OpenColorIO.h
user: login: doug-walker
body: Please add the following to this and to virtualViewsAreEqual:

Note that the comparison is only on the strings contained in the view definition, the function does not attempt to compare that the color spaces or view transforms being referenced are identical (only that they have the same name).
created_at: 2025-05-15T22:38:48Z
author_association: COLLABORATOR
diff_hunk: @@ -8933,6 +9243,238 @@ OCIO_ADD_TEST(Config, virtual_display_exceptions)
                           " which is not defined.");
 }
 
path: tests/cpu/Config_tests.cpp
user: login: doug-walker
body: @annieln , sorry, my request was not clear.  What I meant was that I wanted you to move your new tests to Display_tests.cpp.  This is because Config_tests.cpp is already too big and so we try to avoid adding new tests there if they could easily go somewhere else.

So please move the new test "compare_displays" and restore the other test "display_view_order".
created_at: 2025-05-17T01:26:29Z
author_association: COLLABORATOR
diff_hunk: @@ -3749,6 +3749,271 @@ OCIO_ADD_TEST(Config, display_view_order)
     OCIO_CHECK_EQUAL(std::string(config->getView("sRGB_B", 1)), "View_1");
 }
 
path: tests/cpu/Config_tests.cpp
user: login: doug-walker
body: Sorry, my instructions were not clear.  Please move the new test "compare_virtual_displays" to Display_tests.cpp and restore the old test "virtual_display_exceptions" to Config_tests.cpp.
created_at: 2025-05-17T01:29:18Z
author_association: COLLABORATOR
diff_hunk: @@ -8933,6 +9243,238 @@ OCIO_ADD_TEST(Config, virtual_display_exceptions)
                           " which is not defined.");
 }
 
path: tests/cpu/Config_tests.cpp
user: login: annieln
body: Updated, sorry about that!
created_at: 2025-05-17T10:25:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -852,13 +852,20 @@ class OCIOEXPORT Config
      * \ref Config::validate will throw if the config does not contain
      * the matching display color space.
      */
+
+    /// Check if a view within a given display is referencing one of the config's shared views.
+    bool viewIsShared(const char * dispName, const char * viewName) const;
path: include/OpenColorIO/OpenColorIO.h
user: login: cozdas
body: This is a comment more on the original issue rather than the PR:

The wording order `{object(s)}{Is/Are}{State}` for the function name is different than the more typical convention of `{is/are}{Object(s)}{State}`. Config class already has function in the second form, like Config::isColorSpaceUsed(), Config::isColorSpaceLinear(). We also have functions named in yet another form like Config::isInactiveColorSpace().

This is a question for @doug-walker I guess: Having the other forms already in the API, do we really want to use this form? Personally, I believe that `isViewShared()` would be a better name choice.
created_at: 2025-05-20T04:30:34Z
author_association: COLLABORATOR
diff_hunk: @@ -883,6 +890,23 @@ class OCIOEXPORT Config
     int getNumViews(const char * display, const char * colorspaceName) const;
     const char * getView(const char * display, const char * colorspaceName, int index) const;
 
+    /**
+     * \brief Compare views in a pair of configs.
+     *
+     * Will return false if either of the views does not exist. This will return true even
+     * if the view is display-defined in one config and a reference to a shared view in the
+     * other config (both within the same display), as long as the contents match. The
+     * description text (if any) is ignored, since it is not a functional difference.
+     * 
+     * Note that the comparison is only on the strings contained in the view definition,
+     * the function does not attempt to compare that the color spaces or view transforms
+     * being referenced are identical (only that they have the same name).
+     */
+    static bool ViewsAreEqual(const ConstConfigRcPtr & first,
path: include/OpenColorIO/OpenColorIO.h
user: login: cozdas
body: `areViewsEqual()` ?
created_at: 2025-05-20T04:31:07Z
author_association: COLLABORATOR
diff_hunk: @@ -900,6 +924,17 @@ class OCIOEXPORT Config
     /// Returns the description attribute of a (display, view) pair.
     const char * getDisplayViewDescription(const char * display, const char * view) const noexcept;
 
+    /**
+     * \brief Determine if a display and view exist.
+     *
+     * This returns false if either the display or view doesn't exist. It works regardless
+     * of whether the display or view are active, and it works regardless of whether the
+     * view is display-defined or if the display has this as a shared view. It will only
+     * check config-level shared views if dispName is null. It will not check config level
+     * shared views if dispName is not null.
+     */
+    bool displayHasView(const char * dispName, const char * viewName) const;
path: include/OpenColorIO/OpenColorIO.h
user: login: cozdas
body: Since this function is valid for the dispName being nullptr, I think we can drop the display in the name of the function making it more general. Following the previous arguments I'd suggest `hasView()`


created_at: 2025-05-20T04:33:32Z
author_association: COLLABORATOR
diff_hunk: @@ -963,6 +998,18 @@ class OCIOEXPORT Config
      *
      */
 
+    /**
+     * \brief Determine if a virtual view exists.
+     *
+     * This returns false if the virtual view doesn't exist. It works regardless of
+     * whether the virtual view is active, and it works regardless of whether the virtual
+     * view is display-defined or if the display has this as a shared virtual view.
+     */
+    bool hasVirtualView(const char * viewName) const;
+
+    /// Check if a given virtual view is referencing one of the config's shared views.
+    bool virtualViewIsShared(const char * viewName) const;
path: include/OpenColorIO/OpenColorIO.h
user: login: cozdas
body: `isVirtualViewShared()` ?
created_at: 2025-05-20T04:34:29Z
author_association: COLLABORATOR
diff_hunk: @@ -977,6 +1024,23 @@ class OCIOEXPORT Config
     /// Get the view name at a specific index.
     const char * getVirtualDisplayView(ViewType type, int index) const noexcept;
 
+    /**
+     * \brief Compare virtual views in a pair of configs.
+     *
+     * Will return false if either of the virtual views does not exist. This will return true
+     * even if the virtual view is display-defined in one config and a reference to a shared
+     * virtual view in the other config, as long as the contents match.
+     * 
+     * The description text (if any) is ignored, since it is not a functional difference.
+     * 
+     * Note that the comparison is only on the strings contained in the view definition,
+     * the function does not attempt to compare that the color spaces or view transforms
+     * being referenced are identical (only that they have the same name).
+     */
+    static bool VirtualViewsAreEqual(const ConstConfigRcPtr & first,
path: include/OpenColorIO/OpenColorIO.h
user: login: cozdas
body: `areVirtualViewsEqual()` ?
created_at: 2025-05-20T04:35:00Z
author_association: COLLABORATOR
diff_hunk: @@ -3305,6 +3305,21 @@ void Config::setViewingRules(ConstViewingRulesRcPtr viewingRules)
     getImpl()->resetCacheIDs();
 }
 
+bool Config::viewIsShared(const char * dispName, const char * viewName) const
+{
+
path: src/OpenColorIO/Config.cpp
user: login: cozdas
body: we're checking the `getView()` result against null or empty string, so the passed parameter `viewName` should be checked here as well.
created_at: 2025-05-20T04:42:19Z
author_association: COLLABORATOR
diff_hunk: @@ -3358,6 +3373,19 @@ void Config::removeSharedView(const char * view)
     }
 }
 
+void Config::clearSharedViews()
+{
+    int numViews = getNumViews(VIEW_SHARED, nullptr);
+    for (int v = numViews - 1; v >= 0; v--)
+    {
+        const char * sharedViewName = getView(VIEW_SHARED, nullptr, v);
+        if (sharedViewName && *sharedViewName)
+        {
+            removeSharedView(sharedViewName);
path: src/OpenColorIO/Config.cpp
user: login: cozdas
body: A little bit inefficient implementation as removeSharedView() will call FindView() to loop again to find it but it's fine as run-time cost will be very small.
created_at: 2025-05-20T04:51:23Z
author_association: COLLABORATOR
diff_hunk: @@ -3695,6 +3775,28 @@ void Config::clearDisplays()
     getImpl()->resetCacheIDs();
 }
 
+bool Config::hasVirtualView(const char * viewName) const
+{
+    const char * cs = getVirtualDisplayViewColorSpaceName(viewName);
+
+    // All views must have a color space, so if it's not empty, the view exists.
+    return (cs && *cs);
+}
+
+bool Config::virtualViewIsShared(const char * viewName) const
+{
+    for (int v = 0; v < getVirtualDisplayNumViews(VIEW_SHARED); v++)
path: src/OpenColorIO/Config.cpp
user: login: cozdas
body: `viewName` should be checked against being null or empty too.
created_at: 2025-05-20T04:57:04Z
author_association: COLLABORATOR
diff_hunk: @@ -852,13 +852,20 @@ class OCIOEXPORT Config
      * \ref Config::validate will throw if the config does not contain
      * the matching display color space.
      */
+
+    /// Check if a view within a given display is referencing one of the config's shared views.
+    bool viewIsShared(const char * dispName, const char * viewName) const;
path: include/OpenColorIO/OpenColorIO.h
user: login: doug-walker
body: I don't object to any of the proposed names. However, the time for that discussion would have been *before* we assigned it to a volunteer to implement.  I would not object to someone else renaming these before 2.5.0 is released, but I would feel bad to ask Annie to do this.
created_at: 2025-05-22T03:30:52Z
author_association: COLLABORATOR
diff_hunk: @@ -852,13 +852,20 @@ class OCIOEXPORT Config
      * \ref Config::validate will throw if the config does not contain
      * the matching display color space.
      */
+
+    /// Check if a view within a given display is referencing one of the config's shared views.
+    bool viewIsShared(const char * dispName, const char * viewName) const;
path: include/OpenColorIO/OpenColorIO.h
user: login: cozdas
body: Fair point. If we decide to, we can rename the functions before releasing a new version.
created_at: 2025-05-22T05:16:18Z
author_association: COLLABORATOR

PR comments:
user: login: annieln
created_at: 2025-02-18T00:37:51Z
body: Hi, Doug! I haven't forgotten about this issue, but it has been a while, so I may have forgotten some items or fixes. Let me know if that's the case.

While writing tests, I found that the `getVirtualDisplayViewTransformName` method returns `''` for shared virtual views, even when a view transform is defined. I modified the method similar to my solution for `getVirtualDisplayViewColorSpaceName`, but wanted to flag and relate to #2082.
author_association: CONTRIBUTOR
user: login: doug-walker
created_at: 2025-02-18T23:00:56Z
body: Thank you for this update @annieln !  We have a few large high-priority projects happening at the moment, so it may take a few weeks for us to review your latest changes.  I notice that the Linux CI builds failed but that looks to be unrelated to your work. Once we update the build scripts, I will re-run the Linux checks on this PR.  Thanks again!
author_association: COLLABORATOR
user: login: annieln
created_at: 2025-05-15T13:14:32Z
body: Hi Doug! I'm hoping I can finish up this task for Dev Days 2025 - would we be able to re-run the Linux checks here?
author_association: CONTRIBUTOR
user: login: carolalynn
created_at: 2025-05-15T15:37:59Z
body: Hi Annie! I believe if you update this branch against main, it will re-run the CI! :) 
author_association: COLLABORATOR
user: login: annieln
created_at: 2025-05-16T14:30:57Z
body: I believe I've addressed all the suggested changes - let me know if I missed anything or if there's anything else!
author_association: CONTRIBUTOR
user: login: doug-walker
created_at: 2025-05-17T01:34:19Z
body: Your tests look great!  Please ignore the failed Linux CI test, that is due to an unrelated problem in our build setup.
author_association: COLLABORATOR
user: login: cozdas
created_at: 2025-05-20T05:01:35Z
body: Great work!
I added few comments, please check.
author_association: COLLABORATOR
user: login: doug-walker
created_at: 2025-05-22T03:34:17Z
body: @annieln, Cuneyt has raised a valid point about the Strcasecmp function calls. The behavior is undefined (and could crash) if a null pointer is passed as an argument.  Please add the two checks he requested in Config.cpp and we should be good to merge this.  Thanks again!
author_association: COLLABORATOR
user: login: annieln
created_at: 2025-05-24T10:12:16Z
body: I've added the null/empty view name check to `viewIsShared` and `virtualViewIsShared`. I've also included a couple of lines for these checks in the `virtual_display` test in Config_tests.cpp. Let me know if there's anything else I should address!
author_association: CONTRIBUTOR
user: login: doug-walker
created_at: 2025-05-26T18:11:53Z
body: Merging this as the issues with the failing documentation-related CI tests are unrelated to this PR.

Thanks again, @annieln !!
author_association: COLLABORATOR