loader: Remove duplicate search paths on posix
authorDavid Hubbard <david.c.hubbard@gmail.com>
Fri, 21 Dec 2018 18:56:49 +0000 (12:56 -0600)
committerLenny Komow <lenny@lunarg.com>
Wed, 8 Apr 2020 20:08:06 +0000 (14:08 -0600)
Windows platforms remove duplicate search paths due to registry keys
that are not device-specific but need to be searched. For the most
part, mac and linux do not need to do that. But if duplicate paths
crop up on a posix platform, it can cause the same device showing up
twice in the vkEnumeratePhysicalDevices results. There may be other
places it causes unexpected results as well.

VkPhysicalDeviceIDProperties.deviceUUID can be used later to tell that
the two entries are actually the same device and not a multi-GPU
system, but finding the root cause at that point is going to be very
difficult.

loader/loader.c

index 143be13d0870722f76b191679692464defa9ca79..b84935b7e8ef340c54d4dd058412561648ac532a 100644 (file)
@@ -3858,6 +3858,31 @@ static VkResult ReadDataFilesInSearchPaths(const struct loader_instance *inst, e
 #endif
     }
 
+    // Remove duplicate paths, or it would result in duplicate extensions, duplicate devices, etc.
+    // This uses minimal memory, but is O(N^2) on the number of paths. Expect only a few paths.
+    char path_sep_str[2] = { PATH_SEPARATOR, '\0' };
+    size_t search_path_updated_size = strlen(search_path);
+    for (size_t first = 0; first < search_path_updated_size - 1; ) {
+        size_t first_end = first + 1;
+        first_end += strcspn(&search_path[first_end], path_sep_str);
+        for (size_t second = first_end + 1; second < search_path_updated_size; ) {
+            size_t second_end = second + 1;
+            second_end += strcspn(&search_path[second_end], path_sep_str);
+            if (first_end - first == second_end - second && !strncmp(&search_path[first], &search_path[second], second_end - second)) {
+                // Found duplicate. Include PATH_SEPARATOR in second_end, then erase it from search_path.
+                if (search_path[second_end] == PATH_SEPARATOR) {
+                    second_end++;
+                }
+                memmove(&search_path[second], &search_path[second_end], search_path_updated_size - second_end + 1);
+                search_path_updated_size -= second_end - second;
+            } else {
+                second = second_end + 1;
+            }
+        }
+        first = first_end + 1;
+    }
+    search_path_size = search_path_updated_size;
+
     // Print out the paths being searched if debugging is enabled
     if (search_path_size > 0) {
         loader_log(inst, VK_DEBUG_REPORT_DEBUG_BIT_EXT, 0,