vulkaninfo: new vkconfig_output backdend
[platform/upstream/Vulkan-Tools.git] / vulkaninfo / vulkaninfo.cpp
index 7068548..a0b843a 100644 (file)
@@ -57,11 +57,10 @@ void DumpExtensions(Printer &p, std::string layer_name, std::vector<VkExtensionP
         }
     }
 
-    p.ArrayStart(layer_name + " Extensions", extensions.size());
+    ObjectWrapper obj(p, layer_name + " Extensions", extensions.size());
     for (auto &ext : extensions) {
         p.PrintExtension(ext.extensionName, ext.specVersion, max_length);
     }
-    p.ArrayEnd();
 }
 
 void DumpLayers(Printer &p, std::vector<LayerExtensionList> layers, const std::vector<std::unique_ptr<AppGpu>> &gpus) {
@@ -70,74 +69,89 @@ void DumpLayers(Printer &p, std::vector<LayerExtensionList> layers, const std::v
         const char *b = right.layer_properties.layerName;
         return a && (!b || std::strcmp(a, b) < 0);
     });
+    switch (p.Type()) {
+        case OutputType::text:
+        case OutputType::html: {
+            p.SetHeader();
+            ArrayWrapper arr(p, "Layers", layers.size());
+            p.IndentDecrease();
+            for (auto &layer : layers) {
+                auto v_str = VkVersionString(layer.layer_properties.specVersion);
+                auto props = layer.layer_properties;
+
+                std::string header = p.DecorateAsType(props.layerName) + " (" + props.description + ") Vulkan version " +
+                                     p.DecorateAsValue(v_str) + ", layer version " +
+                                     p.DecorateAsValue(std::to_string(props.implementationVersion));
+                ObjectWrapper obj(p, header);
+                DumpExtensions(p, "Layer", layer.extension_properties);
+
+                ArrayWrapper arr(p, "Devices", gpus.size());
+                for (auto &gpu : gpus) {
+                    p.PrintKeyValue("GPU id", gpu->id, 0, gpu->props.deviceName);
+                    auto exts = gpu->AppGetPhysicalDeviceLayerExtensions(props.layerName);
+                    DumpExtensions(p, "Layer-Device", exts);
+                    p.AddNewline();
+                }
+            }
+            p.IndentIncrease();
+            break;
+        }
 
-    if (p.Type() == OutputType::text || p.Type() == OutputType::html) {
-        p.SetHeader().ArrayStart("Layers", layers.size());
-        p.IndentDecrease();
-        for (auto &layer : layers) {
-            auto v_str = VkVersionString(layer.layer_properties.specVersion);
-            auto props = layer.layer_properties;
-
-            std::string header;
-            if (p.Type() == OutputType::text)
-                header = std::string(props.layerName) + " (" + props.description + ") Vulkan version " + v_str +
-                         ", layer version " + std::to_string(props.implementationVersion);
-            else if (p.Type() == OutputType::html)
-                header = std::string("<span class='type'>") + props.layerName + "</span> (" + props.description +
-                         ") Vulkan version <span class='val'>" + v_str + "</span>, layer version <span class='val'>" +
-                         std::to_string(props.implementationVersion) + "</span>";
-
-            p.ObjectStart(header);
-            DumpExtensions(p, "Layer", layer.extension_properties);
-
-            p.ArrayStart("Devices", gpus.size());
-            for (auto &gpu : gpus) {
-                p.PrintElement(std::string("GPU id \t: ") + std::to_string(gpu->id), gpu->props.deviceName);
-                auto exts = gpu->AppGetPhysicalDeviceLayerExtensions(props.layerName);
-                DumpExtensions(p, "Layer-Device", exts);
-                p.AddNewline();
+        case OutputType::json: {
+            ArrayWrapper arr(p, "ArrayOfVkLayerProperties", layers.size());
+            int i = 0;
+            for (auto &layer : layers) {
+                p.SetElementIndex(i++);
+                DumpVkLayerProperties(p, "layerProperty", layer.layer_properties);
             }
-            p.ArrayEnd();
-            p.ObjectEnd();
+            break;
         }
-        p.IndentIncrease();
-        p.ArrayEnd();
-    } else if (p.Type() == OutputType::json) {
-        p.ArrayStart("ArrayOfVkLayerProperties", layers.size());
-        int i = 0;
-        for (auto &layer : layers) {
-            p.SetElementIndex(i++);
-            DumpVkLayerProperties(p, "layerProperty", layer.layer_properties);
+        case OutputType::vkconfig_output: {
+            ObjectWrapper obj(p, "Layer Properties");
+            for (auto &layer : layers) {
+                ObjectWrapper obj_name(p, layer.layer_properties.layerName);
+                p.PrintKeyString("layerName", layer.layer_properties.layerName, 21);
+                p.PrintKeyString("version", VkVersionString(layer.layer_properties.specVersion), 21);
+                p.PrintKeyValue("implementation version", layer.layer_properties.implementationVersion, 21);
+                p.PrintKeyString("description", layer.layer_properties.description, 21);
+                DumpExtensions(p, "Layer", layer.extension_properties);
+                ObjectWrapper obj_devices(p, "Devices");
+                for (auto &gpu : gpus) {
+                    ObjectWrapper obj(p, gpu->props.deviceName);
+                    p.PrintKeyValue("GPU id", gpu->id, 0, gpu->props.deviceName);
+                    auto exts = gpu->AppGetPhysicalDeviceLayerExtensions(layer.layer_properties.layerName);
+                    DumpExtensions(p, "Layer-Device", exts);
+                }
+            }
+            break;
         }
-        p.ArrayEnd();
     }
 }
 
 void DumpSurfaceFormats(Printer &p, AppInstance &inst, AppSurface &surface) {
-    int i = 0;
+    std::vector<VkSurfaceFormatKHR> formats;
     if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) {
-        p.ArrayStart("Formats", surface.surf_formats2.size());
         for (auto &format : surface.surf_formats2) {
-            p.SetElementIndex(i++);
-            DumpVkSurfaceFormatKHR(p, "SurfaceFormat", format.surfaceFormat);
+            formats.push_back(format.surfaceFormat);
         }
-        p.ArrayEnd();
     } else {
-        p.ArrayStart("Formats", surface.surf_formats.size());
         for (auto &format : surface.surf_formats) {
-            p.SetElementIndex(i++);
-            DumpVkSurfaceFormatKHR(p, "SurfaceFormat", format);
+            formats.push_back(format);
         }
-        p.ArrayEnd();
+    }
+    ObjectWrapper obj(p, "Formats", formats.size());
+    int i = 0;
+    for (auto &format : formats) {
+        p.SetElementIndex(i++);
+        DumpVkSurfaceFormatKHR(p, "SurfaceFormat", format);
     }
 }
 
 void DumpPresentModes(Printer &p, AppSurface &surface) {
-    p.ArrayStart("Present Modes", surface.surf_present_modes.size());
+    ArrayWrapper arr(p, "Present Modes", surface.surf_present_modes.size());
     for (auto &mode : surface.surf_present_modes) {
-        p.SetAsType().PrintElement(VkPresentModeKHRString(mode));
+        p.SetAsType().PrintString(VkPresentModeKHRString(mode));
     }
-    p.ArrayEnd();
 }
 
 void DumpSurfaceCapabilities(Printer &p, AppInstance &inst, AppGpu &gpu, AppSurface &surface) {
@@ -146,16 +160,15 @@ void DumpSurfaceCapabilities(Printer &p, AppInstance &inst, AppGpu &gpu, AppSurf
     DumpVkSurfaceCapabilitiesKHR(p, "VkSurfaceCapabilitiesKHR", surf_cap);
 
     if (inst.CheckExtensionEnabled(VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME)) {
-        p.SetSubHeader().ObjectStart("VkSurfaceCapabilities2EXT");
+        p.SetSubHeader();
+        ObjectWrapper obj(p, "VkSurfaceCapabilities2EXT");
         {
-            p.ObjectStart("supportedSurfaceCounters");
-            if (surface.surface_capabilities2_ext.supportedSurfaceCounters == 0) p.PrintElement("None");
+            ArrayWrapper arr(p, "supportedSurfaceCounters");
+            if (surface.surface_capabilities2_ext.supportedSurfaceCounters == 0) p.PrintString("None");
             if (surface.surface_capabilities2_ext.supportedSurfaceCounters & VK_SURFACE_COUNTER_VBLANK_EXT) {
-                p.SetAsType().PrintElement("VK_SURFACE_COUNTER_VBLANK_EXT");
+                p.SetAsType().PrintString("VK_SURFACE_COUNTER_VBLANK_EXT");
             }
-            p.ObjectEnd();
         }
-        p.ObjectEnd();  // VkSurfaceCapabilities2EXT
     }
     if (inst.CheckExtensionEnabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME)) {
         chain_iterator_surface_capabilities2(p, inst, gpu, surface.surface_capabilities2_khr.pNext, inst.vk_version);
@@ -163,30 +176,23 @@ void DumpSurfaceCapabilities(Printer &p, AppInstance &inst, AppGpu &gpu, AppSurf
 }
 
 void DumpSurface(Printer &p, AppInstance &inst, AppGpu &gpu, AppSurface &surface, std::set<std::string> surface_types) {
-    std::string header;
-    if (p.Type() == OutputType::text)
-        header = std::string("GPU id : ") + std::to_string(gpu.id) + " (" + gpu.props.deviceName + ")";
-    else if (p.Type() == OutputType::html)
-        header = std::string("GPU id : <span class='val'>") + std::to_string(gpu.id) + "</span> (" + gpu.props.deviceName + ")";
-    p.ObjectStart(header);
+    ObjectWrapper obj(p, std::string("GPU id : ") + p.DecorateAsValue(std::to_string(gpu.id)) + " (" + gpu.props.deviceName + ")");
 
     if (surface_types.size() == 0) {
-        p.SetAsType().PrintKeyValue("Surface type", surface.surface_extension.name);
+        p.SetAsType().PrintKeyString("Surface type", "No type found");
+    } else if (surface_types.size() == 1) {
+        p.SetAsType().PrintKeyString("Surface type", surface.surface_extension.name);
     } else {
-        p.ArrayStart("Surface types", surface_types.size());
+        ArrayWrapper arr(p, "Surface types", surface_types.size());
         for (auto &name : surface_types) {
-            p.PrintElement(name);
+            p.PrintString(name);
         }
-        p.ArrayEnd();
     }
 
     DumpSurfaceFormats(p, inst, surface);
-
     DumpPresentModes(p, surface);
-
     DumpSurfaceCapabilities(p, inst, gpu, surface);
 
-    p.ObjectEnd();
     p.AddNewline();
 }
 
@@ -204,8 +210,10 @@ bool operator==(AppSurface const &a, AppSurface const &b) {
 
 void DumpPresentableSurfaces(Printer &p, AppInstance &inst, const std::vector<std::unique_ptr<AppGpu>> &gpus,
                              const std::vector<std::unique_ptr<AppSurface>> &surfaces) {
-    p.SetHeader().ObjectStart("Presentable Surfaces");
+    p.SetHeader();
+    ObjectWrapper obj(p, "Presentable Surfaces");
     p.IndentDecrease();
+
     std::vector<SurfaceTypeGroup> surface_list;
 
     for (auto &surface : surfaces) {
@@ -233,7 +241,6 @@ void DumpPresentableSurfaces(Printer &p, AppInstance &inst, const std::vector<st
         DumpSurface(p, inst, *group.gpu, *group.surface, group.surface_types);
     }
     p.IndentIncrease();
-    p.ObjectEnd();
     p.AddNewline();
 }
 
@@ -241,157 +248,159 @@ void DumpGroups(Printer &p, AppInstance &inst) {
     if (inst.CheckExtensionEnabled(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME)) {
         auto groups = GetGroups(inst);
         if (groups.size() == 0) {
-            p.SetHeader().ObjectStart("Groups");
-            p.PrintElement("No Device Groups Found");
-            p.ObjectEnd();
+            p.SetHeader();
+            ObjectWrapper obj(p, "Groups");
+            p.PrintString("No Device Groups Found");
             p.AddNewline();
             return;
         }
-        p.SetHeader().ObjectStart("Groups");
+
+        p.SetHeader();
+        ObjectWrapper obj(p, "Device Groups");
+        p.IndentDecrease();
         int group_id = 0;
         for (auto &group : groups) {
-            p.ObjectStart("Device Group Properties (Group " + std::to_string(group_id) + ")");
+            ObjectWrapper obj(p, "Group " + std::to_string(group_id));
             auto group_props = GetGroupProps(group);
-            p.ArrayStart("physicalDeviceCount", group.physicalDeviceCount);
-            int id = 0;
-            for (auto &prop : group_props) {
-                std::string device_out = prop.deviceName;
-                if (p.Type() == OutputType::text) {
-                    device_out += " (ID: " + std::to_string(id++) + ")";
-                } else if (p.Type() == OutputType::html) {
-                    device_out += " (ID: <span class='val'>" + std::to_string(id++) + "</span>)";
+            {
+                ObjectWrapper obj(p, "Properties");
+                {
+                    ArrayWrapper arr(p, "physicalDevices", group.physicalDeviceCount);
+                    int id = 0;
+                    for (auto &prop : group_props) {
+                        p.PrintString(std::string(prop.deviceName) + " (ID: " + p.DecorateAsValue(std::to_string(id++)) + ")");
+                    }
                 }
-                p.PrintElement(device_out);
+                p.PrintKeyValue("subsetAllocation", group.subsetAllocation);
             }
-            p.ArrayEnd();
-            p.PrintKeyValue("subsetAllocation", group.subsetAllocation);
-            p.ObjectEnd();
             p.AddNewline();
 
-            p.ObjectStart("Device Group Present Capabilities (Group " + std::to_string(group_id) + ")");
             auto group_capabilities = GetGroupCapabilities(inst, group);
             if (group_capabilities.first == false) {
-                p.PrintElement("Group does not support VK_KHR_device_group, skipping printing capabilities");
+                p.PrintKeyString("Present Capabilities",
+                                 "Group does not support VK_KHR_device_group, skipping printing present capabilities");
             } else {
+                ObjectWrapper obj(p, "Present Capabilities");
                 for (uint32_t i = 0; i < group.physicalDeviceCount; i++) {
-                    std::string device_out;
-                    if (p.Type() == OutputType::text) {
-                        device_out = std::string(group_props[i].deviceName) + " (ID: " + std::to_string(i) + ")";
-                    } else if (p.Type() == OutputType::html) {
-                        device_out =
-                            std::string(group_props[i].deviceName) + " (ID: <span class='val'>" + std::to_string(i) + "</span>)";
-                    }
-                    p.PrintElement(device_out);
-                    p.ObjectStart("Can present images from the following devices");
+                    ObjectWrapper obj(
+                        p, std::string(group_props[i].deviceName) + " (ID: " + p.DecorateAsValue(std::to_string(i)) + ")");
+                    ArrayWrapper arr(p, "Can present images from the following devices", group.physicalDeviceCount);
+
                     for (uint32_t j = 0; j < group.physicalDeviceCount; j++) {
-                        uint32_t mask = 1U << j;
+                        uint32_t mask = 1 << j;
                         if (group_capabilities.second.presentMask[i] & mask) {
-                            if (p.Type() == OutputType::text)
-                                p.PrintElement(std::string(group_props[j].deviceName) + " (ID: " + std::to_string(j) + ")");
-                            if (p.Type() == OutputType::html)
-                                p.PrintElement(std::string(group_props[j].deviceName) + " (ID: <span class='val'>" +
-                                               std::to_string(j) + "</span>)");
+                            p.PrintString(std::string(group_props[j].deviceName) + " (ID: " + p.DecorateAsValue(std::to_string(j)) +
+                                          ")");
                         }
                     }
-                    p.ObjectEnd();
                 }
                 DumpVkDeviceGroupPresentModeFlagsKHR(p, "Present modes", group_capabilities.second.modes);
             }
-            p.ObjectEnd();
             p.AddNewline();
             group_id++;
         }
-        p.ObjectEnd();
+        p.IndentIncrease();
         p.AddNewline();
     }
 }
 
 void GpuDumpProps(Printer &p, AppGpu &gpu) {
     auto props = gpu.GetDeviceProperties();
-    p.SetSubHeader().ObjectStart("VkPhysicalDeviceProperties");
-    p.PrintKeyValue("apiVersion", props.apiVersion, 14, VkVersionString(props.apiVersion));
-    p.PrintKeyValue("driverVersion", props.driverVersion, 14, to_hex_str(props.driverVersion));
-    if (p.Type() == OutputType::json) {
-        p.PrintKeyValue("vendorID", props.vendorID, 14);
-        p.PrintKeyValue("deviceID", props.deviceID, 14);
-        p.PrintKeyValue("deviceType", props.deviceType, 14);
-    } else {
-        p.PrintKeyValue("vendorID", to_hex_str(props.vendorID), 14);
-        p.PrintKeyValue("deviceID", to_hex_str(props.deviceID), 14);
+    p.SetSubHeader();
+    {
+        ObjectWrapper obj(p, "VkPhysicalDeviceProperties");
+        p.PrintKeyValue("apiVersion", props.apiVersion, 14, VkVersionString(props.apiVersion));
+        p.PrintKeyValue("driverVersion", props.driverVersion, 14, to_hex_str(props.driverVersion));
+        p.PrintKeyString("vendorID", to_hex_str(props.vendorID), 14);
+        p.PrintKeyString("deviceID", to_hex_str(props.deviceID), 14);
         p.PrintKeyString("deviceType", VkPhysicalDeviceTypeString(props.deviceType), 14);
-    }
-    p.PrintKeyString("deviceName", props.deviceName, 14);
-    if (p.Type() == OutputType::json) {
-        p.ArrayStart("pipelineCacheUUID");
-        for (uint32_t i = 0; i < VK_UUID_SIZE; ++i) {
-            p.PrintElement(static_cast<uint32_t>(props.pipelineCacheUUID[i]));
+        p.PrintKeyString("deviceName", props.deviceName, 14);
+        if (p.Type() == OutputType::vkconfig_output) {
+            ArrayWrapper arr(p, "pipelineCacheUUID", VK_UUID_SIZE);
+            for (uint32_t i = 0; i < VK_UUID_SIZE; ++i) {
+                p.PrintElement(static_cast<uint32_t>(props.pipelineCacheUUID[i]));
+            }
         }
-        p.ArrayEnd();
     }
     p.AddNewline();
-    if (p.Type() != OutputType::json) {
-        p.ObjectEnd();  // limits and sparse props are not sub objects in the text and html output
-    }
-
-    if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
-        DumpVkPhysicalDeviceLimits(p, "VkPhysicalDeviceLimits", gpu.props2.properties.limits);
-    } else {
-        DumpVkPhysicalDeviceLimits(p, "VkPhysicalDeviceLimits", gpu.props.limits);
-    }
+    DumpVkPhysicalDeviceLimits(p, "VkPhysicalDeviceLimits",
+                               gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)
+                                   ? gpu.props2.properties.limits
+                                   : gpu.props.limits);
     p.AddNewline();
-    if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
-        DumpVkPhysicalDeviceSparseProperties(p, "VkPhysicalDeviceSparseProperties", gpu.props2.properties.sparseProperties);
-    } else {
-        DumpVkPhysicalDeviceSparseProperties(p, "VkPhysicalDeviceSparseProperties", gpu.props.sparseProperties);
-    }
+    DumpVkPhysicalDeviceSparseProperties(p, "VkPhysicalDeviceSparseProperties",
+                                         gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)
+                                             ? gpu.props2.properties.sparseProperties
+                                             : gpu.props.sparseProperties);
     p.AddNewline();
-    if (p.Type() == OutputType::json) {
-        p.ObjectEnd();  // limits and sparse props are sub objects in the json output
+    if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
+        void *place = gpu.props2.pNext;
+        chain_iterator_phys_device_props2(p, gpu.inst, gpu, place, gpu.api_version);
+        p.AddNewline();
     }
-
-    if (p.Type() != OutputType::json) {
-        if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
-            void *place = gpu.props2.pNext;
-            chain_iterator_phys_device_props2(p, gpu.inst, gpu, place, gpu.api_version);
+}
+void GpuDumpPropsJson(Printer &p, AppGpu &gpu) {
+    auto props = gpu.GetDeviceProperties();
+    ObjectWrapper obj(p, "VkPhysicalDeviceProperties");
+    p.PrintKeyValue("apiVersion", props.apiVersion, 14, VkVersionString(props.apiVersion));
+    p.PrintKeyValue("driverVersion", props.driverVersion, 14, to_hex_str(props.driverVersion));
+    p.PrintKeyValue("vendorID", props.vendorID, 14);
+    p.PrintKeyValue("deviceID", props.deviceID, 14);
+    p.PrintKeyValue("deviceType", props.deviceType, 14);
+    p.PrintKeyString("deviceName", props.deviceName, 14);
+    {
+        ArrayWrapper arr(p, "pipelineCacheUUID", VK_UUID_SIZE);
+        for (uint32_t i = 0; i < VK_UUID_SIZE; ++i) {
+            p.PrintElement(static_cast<uint32_t>(props.pipelineCacheUUID[i]));
         }
     }
-    p.AddNewline();
+
+    DumpVkPhysicalDeviceLimits(p, "VkPhysicalDeviceLimits",
+                               gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)
+                                   ? gpu.props2.properties.limits
+                                   : gpu.props.limits);
+    DumpVkPhysicalDeviceSparseProperties(p, "VkPhysicalDeviceSparseProperties",
+                                         gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)
+                                             ? gpu.props2.properties.sparseProperties
+                                             : gpu.props.sparseProperties);
 }
+
 void GpuDumpQueueProps(Printer &p, std::vector<SurfaceExtension> &surfaces, AppQueueFamilyProperties &queue) {
-    p.SetElementIndex(static_cast<int>(queue.queue_index)).SetSubHeader().ObjectStart("queueProperties");
-    if (p.Type() == OutputType::json) {
+    p.SetSubHeader().SetElementIndex(static_cast<int>(queue.queue_index));
+    ObjectWrapper obj(p, "queueProperties");
+    if (p.Type() == OutputType::vkconfig_output) {
         DumpVkExtent3D(p, "minImageTransferGranularity", queue.props.minImageTransferGranularity);
     } else {
         p.PrintKeyValue("minImageTransferGranularity", queue.props.minImageTransferGranularity, 27);
     }
     p.PrintKeyValue("queueCount", queue.props.queueCount, 27);
-    if (p.Type() == OutputType::json) {
-        p.PrintKeyValue("queueFlags", queue.props.queueFlags, 27);
-    } else {
-        p.PrintKeyValue("queueFlags", VkQueueFlagsString(queue.props.queueFlags), 27);
-    }
-
+    p.PrintKeyString("queueFlags", VkQueueFlagsString(queue.props.queueFlags), 27);
     p.PrintKeyValue("timestampValidBits", queue.props.timestampValidBits, 27);
 
-    if (p.Type() != OutputType::json) {
-        if (queue.is_present_platform_agnostic) {
-            p.PrintKeyString("present support", queue.platforms_support_present ? "true" : "false");
-        } else {
-            size_t width = 0;
-            for (auto &surface : surfaces) {
-                if (surface.name.size() > width) width = surface.name.size();
-            }
-            p.ObjectStart("present support");
-            for (auto &surface : surfaces) {
-                p.PrintKeyString(surface.name, surface.supports_present ? "true" : "false", width);
-            }
-            p.ObjectEnd();
+    if (queue.is_present_platform_agnostic) {
+        p.PrintKeyString("present support", queue.platforms_support_present ? "true" : "false");
+    } else {
+        size_t width = 0;
+        for (auto &surface : surfaces) {
+            if (surface.name.size() > width) width = surface.name.size();
+        }
+        ObjectWrapper obj(p, "present support");
+        for (auto &surface : surfaces) {
+            p.PrintKeyString(surface.name, surface.supports_present ? "true" : "false", width);
         }
     }
-    p.ObjectEnd();
+
     p.AddNewline();
 }
 
+void GpuDumpQueuePropsJson(Printer &p, std::vector<SurfaceExtension> &surfaces, AppQueueFamilyProperties &queue) {
+    ObjectWrapper obj(p, "");
+    DumpVkExtent3D(p, "minImageTransferGranularity", queue.props.minImageTransferGranularity);
+    p.PrintKeyValue("queueCount", queue.props.queueCount, 27);
+    p.PrintKeyValue("queueFlags", queue.props.queueFlags, 27);
+    p.PrintKeyValue("timestampValidBits", queue.props.timestampValidBits, 27);
+}
+
 // This prints a number of bytes in a human-readable format according to prefixes of the International System of Quantities (ISQ),
 // defined in ISO/IEC 80000. The prefixes used here are not SI prefixes, but rather the binary prefixes based on powers of 1024
 // (kibi-, mebi-, gibi- etc.).
@@ -419,41 +428,39 @@ std::string NumToNiceStr(const size_t sz) {
     return std::string(buf);
 }
 
+std::string append_human_readible(VkDeviceSize memory) {
+    return std::to_string(memory) + " (" + to_hex_str(memory) + ") (" + NumToNiceStr(static_cast<size_t>(memory)) + ")";
+}
+
 void GpuDumpMemoryProps(Printer &p, AppGpu &gpu) {
-    p.SetHeader().ObjectStart("VkPhysicalDeviceMemoryProperties");
+    p.SetHeader();
+    ObjectWrapper obj(p, "VkPhysicalDeviceMemoryProperties");
     p.IndentDecrease();
-    p.ArrayStart("memoryHeaps", gpu.memory_props.memoryHeapCount);
-    for (uint32_t i = 0; i < gpu.memory_props.memoryHeapCount; ++i) {
-        const VkDeviceSize memSize = gpu.memory_props.memoryHeaps[i].size;
-        std::string mem_size_human_readable = NumToNiceStr(static_cast<size_t>(memSize));
-
-        std::string mem_size_str = std::to_string(memSize) + " (" + to_hex_str(memSize) + ") (" + mem_size_human_readable + ")";
-
-        p.SetElementIndex(static_cast<int>(i)).ObjectStart("memoryHeaps");
-        if (p.Type() != OutputType::json) {
-            p.PrintKeyValue("size", mem_size_str, 6);
-            p.PrintKeyValue("budget", gpu.heapBudget[i], 6);
-            p.PrintKeyValue("usage", gpu.heapUsage[i], 6);
+    {
+        ObjectWrapper obj(p, "memoryHeaps", gpu.memory_props.memoryHeapCount);
+
+        for (uint32_t i = 0; i < gpu.memory_props.memoryHeapCount; ++i) {
+            p.SetElementIndex(static_cast<int>(i));
+            ObjectWrapper obj(p, "memoryHeaps");
+
+            p.PrintKeyString("size", append_human_readible(gpu.memory_props.memoryHeaps[i].size), 6);
+            p.PrintKeyString("budget", append_human_readible(gpu.heapBudget[i]), 6);
+            p.PrintKeyString("usage", append_human_readible(gpu.heapUsage[i]), 6);
             DumpVkMemoryHeapFlags(p, "flags", gpu.memory_props.memoryHeaps[i].flags, 6);
-        } else {
-            p.PrintKeyValue("flags", gpu.memory_props.memoryHeaps[i].flags);
-            p.PrintKeyValue("size", memSize);
         }
-        p.ObjectEnd();
     }
-    p.ArrayEnd();
+    {
+        ObjectWrapper obj(p, "memoryTypes", gpu.memory_props.memoryTypeCount);
+
+        for (uint32_t i = 0; i < gpu.memory_props.memoryTypeCount; ++i) {
+            p.SetElementIndex(static_cast<int>(i));
+            ObjectWrapper obj(p, "memoryTypes");
+            p.PrintKeyValue("heapIndex", gpu.memory_props.memoryTypes[i].heapIndex, 13);
 
-    p.ArrayStart("memoryTypes", gpu.memory_props.memoryTypeCount);
-    for (uint32_t i = 0; i < gpu.memory_props.memoryTypeCount; ++i) {
-        p.SetElementIndex(static_cast<int>(i)).ObjectStart("memoryTypes");
-        p.PrintKeyValue("heapIndex", gpu.memory_props.memoryTypes[i].heapIndex, 13);
-        if (p.Type() == OutputType::json) {
-            p.PrintKeyValue("propertyFlags", gpu.memory_props.memoryTypes[i].propertyFlags, 13);
-        } else {
             auto flags = gpu.memory_props.memoryTypes[i].propertyFlags;
             DumpVkMemoryPropertyFlags(p, "propertyFlags = " + to_hex_str(flags), flags);
 
-            p.ObjectStart("usable for");
+            ArrayWrapper arr(p, "usable for", -1);
             const uint32_t memtype_bit = 1U << i;
 
             // only linear and optimal tiling considered
@@ -504,73 +511,99 @@ void GpuDumpMemoryProps(Printer &p, AppGpu &gpu) {
                 {
                     usable += "None";
                 }
-                p.PrintElement(usable);
+                p.PrintString(usable);
             }
-            p.ObjectEnd();
         }
-
-        p.ObjectEnd();
     }
-    p.ArrayEnd();
+
     p.IndentIncrease();
-    p.ObjectEnd();
     p.AddNewline();
 }
 
+void GpuDumpMemoryPropsJson(Printer &p, AppGpu &gpu) {
+    ObjectWrapper obj(p, "VkPhysicalDeviceMemoryProperties");
+    {
+        ArrayWrapper arr(p, "memoryHeaps", gpu.memory_props.memoryHeapCount);
+        for (uint32_t i = 0; i < gpu.memory_props.memoryHeapCount; ++i) {
+            ObjectWrapper obj(p, "");
+            p.PrintKeyValue("flags", gpu.memory_props.memoryHeaps[i].flags);
+            p.PrintKeyValue("size", gpu.memory_props.memoryHeaps[i].size);
+        }
+    }
+    {
+        ArrayWrapper arr(p, "memoryTypes", gpu.memory_props.memoryTypeCount);
+        for (uint32_t i = 0; i < gpu.memory_props.memoryTypeCount; ++i) {
+            ObjectWrapper obj(p, "");
+            p.PrintKeyValue("heapIndex", gpu.memory_props.memoryTypes[i].heapIndex, 13);
+            p.PrintKeyValue("propertyFlags", gpu.memory_props.memoryTypes[i].propertyFlags, 13);
+        }
+    }
+}
+
 void GpuDumpFeatures(Printer &p, AppGpu &gpu) {
     p.SetHeader();
     DumpVkPhysicalDeviceFeatures(p, "VkPhysicalDeviceFeatures", gpu.features);
     p.AddNewline();
-    if (p.Type() != OutputType::json) {
-        if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
-            void *place = gpu.features2.pNext;
-            chain_iterator_phys_device_features2(p, gpu, place, gpu.api_version);
-        }
+    if (gpu.inst.CheckExtensionEnabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
+        void *place = gpu.features2.pNext;
+        chain_iterator_phys_device_features2(p, gpu, place, gpu.api_version);
     }
 }
 
 void GpuDumpFormatProperty(Printer &p, VkFormat fmt, VkFormatProperties prop) {
-    if (p.Type() == OutputType::text) {
-        p.ObjectStart("Properties");
-    } else if (p.Type() == OutputType::html) {
-        p.SetTitleAsType().ObjectStart(VkFormatString(fmt));
-    } else if (p.Type() == OutputType::json) {
-        p.ObjectStart("");
-    }
-    if (p.Type() == OutputType::html || p.Type() == OutputType::text) {
-        p.SetOpenDetails();
-        DumpVkFormatFeatureFlags(p, "linearTiling", prop.linearTilingFeatures);
-        p.SetOpenDetails();
-        DumpVkFormatFeatureFlags(p, "optimalTiling", prop.optimalTilingFeatures);
-        p.SetOpenDetails();
-        DumpVkFormatFeatureFlags(p, "bufferFeatures", prop.bufferFeatures);
-    } else if (p.Type() == OutputType::json) {
-        p.PrintKeyValue("formatID", fmt);
-        p.PrintKeyValue("linearTilingFeatures", prop.linearTilingFeatures);
-        p.PrintKeyValue("optimalTilingFeatures", prop.optimalTilingFeatures);
-        p.PrintKeyValue("bufferFeatures", prop.bufferFeatures);
-    }
-    p.ObjectEnd();
+    switch (p.Type()) {
+        case OutputType::text: {
+            ObjectWrapper obj(p, "Properties");
+            DumpVkFormatFeatureFlags(p, "linearTiling", prop.linearTilingFeatures);
+            DumpVkFormatFeatureFlags(p, "optimalTiling", prop.optimalTilingFeatures);
+            DumpVkFormatFeatureFlags(p, "bufferFeatures", prop.bufferFeatures);
+            break;
+        }
+        case OutputType::html: {
+            p.SetTitleAsType();
+            ObjectWrapper obj(p, VkFormatString(fmt));
+            p.SetOpenDetails();
+            DumpVkFormatFeatureFlags(p, "linearTiling", prop.linearTilingFeatures);
+            p.SetOpenDetails();
+            DumpVkFormatFeatureFlags(p, "optimalTiling", prop.optimalTilingFeatures);
+            p.SetOpenDetails();
+            DumpVkFormatFeatureFlags(p, "bufferFeatures", prop.bufferFeatures);
+            break;
+        }
+        case OutputType::json: {
+            ObjectWrapper obj(p, "");
+            p.PrintKeyValue("formatID", fmt);
+            p.PrintKeyValue("linearTilingFeatures", prop.linearTilingFeatures);
+            p.PrintKeyValue("optimalTilingFeatures", prop.optimalTilingFeatures);
+            p.PrintKeyValue("bufferFeatures", prop.bufferFeatures);
+            break;
+        }
+        case OutputType::vkconfig_output: {
+            ObjectWrapper obj(p, VkFormatString(fmt));
+            DumpVkFormatFeatureFlags(p, "linearTiling", prop.linearTilingFeatures);
+            DumpVkFormatFeatureFlags(p, "optimalTiling", prop.optimalTilingFeatures);
+            DumpVkFormatFeatureFlags(p, "bufferFeatures", prop.bufferFeatures);
+            break;
+        }
+    }
 }
 
 void GpuDumpToolingInfo(Printer &p, AppGpu &gpu) {
     auto tools = GetToolingInfo(gpu);
     if (tools.size() > 0) {
-        p.SetSubHeader().ObjectStart("Tooling Info");
+        p.SetSubHeader();
+        ObjectWrapper obj(p, "Tooling Info");
         for (auto tool : tools) {
             DumpVkPhysicalDeviceToolPropertiesEXT(p, tool.name, tool);
+            p.AddNewline();
         }
-        p.ObjectEnd();
     }
 }
 
 void GpuDevDump(Printer &p, AppGpu &gpu) {
-    if (p.Type() == OutputType::json) {
-        p.ArrayStart("ArrayOfVkFormatProperties");
-    } else {
-        p.SetHeader().ObjectStart("Format Properties");
-        p.IndentDecrease();
-    }
+    p.SetHeader();
+    ObjectWrapper obj(p, "Format Properties");
+    p.IndentDecrease();
 
     if (p.Type() == OutputType::text) {
         auto fmtPropMap = FormatPropMap(gpu);
@@ -587,27 +620,26 @@ void GpuDevDump(Printer &p, AppGpu &gpu) {
                 continue;
             }
 
-            p.SetElementIndex(counter++).ObjectStart("Common Format Group");
+            p.SetElementIndex(counter++);
+            ObjectWrapper obj(p, "Common Format Group");
             p.IndentDecrease();
-            p.ObjectStart("Formats");
-            for (auto &fmt : prop.second) {
-                p.SetAsType().PrintElement(VkFormatString(fmt));
-            }
-            p.ObjectEnd();
 
+            {
+                ArrayWrapper arr(p, "Formats", prop.second.size());
+                for (auto &fmt : prop.second) {
+                    p.SetAsType().PrintString(VkFormatString(fmt));
+                }
+            }
             GpuDumpFormatProperty(p, VK_FORMAT_UNDEFINED, props);
 
             p.IndentIncrease();
-            p.ObjectEnd();
             p.AddNewline();
         }
 
-        p.ObjectStart("Unsupported Formats");
+        ArrayWrapper arr(p, "Unsupported Formats", unsupported_formats.size());
         for (auto &fmt : unsupported_formats) {
-            p.SetAsType().PrintElement(VkFormatString(fmt));
+            p.SetAsType().PrintString(VkFormatString(fmt));
         }
-        p.ObjectEnd();
-
     } else {
         for (auto &format : gpu.supported_format_ranges) {
             if (gpu.FormatRangeSupported(format)) {
@@ -617,69 +649,78 @@ void GpuDevDump(Printer &p, AppGpu &gpu) {
                     VkFormatProperties props;
                     vkGetPhysicalDeviceFormatProperties(gpu.phys_device, fmt, &props);
 
-                    // if json, don't print format properties that are unsupported
-                    if (p.Type() == OutputType::json &&
-                        (props.linearTilingFeatures || props.optimalTilingFeatures || props.bufferFeatures) == 0)
-                        continue;
-
                     GpuDumpFormatProperty(p, fmt, props);
                 }
             }
         }
     }
 
-    if (p.Type() == OutputType::json) {
-        p.ArrayEnd();
-    } else {
-        p.IndentIncrease();
-        p.ObjectEnd();
-    }
-
+    p.IndentIncrease();
     p.AddNewline();
 }
 
-void DumpGpu(Printer &p, AppGpu &gpu, bool show_formats) {
-    if (p.Type() != OutputType::json) {
-        p.ObjectStart("GPU" + std::to_string(gpu.id));
-        p.IndentDecrease();
-    }
-    GpuDumpProps(p, gpu);
+void GpuDevDumpJson(Printer &p, AppGpu &gpu) {
+    ArrayWrapper arr(p, "ArrayOfVkFormatProperties");
+    for (auto &format : gpu.supported_format_ranges) {
+        if (gpu.FormatRangeSupported(format)) {
+            for (int32_t fmt_counter = format.first_format; fmt_counter <= format.last_format; ++fmt_counter) {
+                VkFormat fmt = static_cast<VkFormat>(fmt_counter);
 
-    if (p.Type() != OutputType::json) {
-        DumpExtensions(p, "Device", gpu.device_extensions);
-        p.AddNewline();
-    }
+                VkFormatProperties props;
+                vkGetPhysicalDeviceFormatProperties(gpu.phys_device, fmt, &props);
 
-    if (p.Type() == OutputType::json) {
-        p.ArrayStart("ArrayOfVkQueueFamilyProperties");
-    } else {
-        p.SetHeader().ObjectStart("VkQueueFamilyProperties");
-    }
-    for (uint32_t i = 0; i < gpu.queue_count; i++) {
-        AppQueueFamilyProperties queue_props = AppQueueFamilyProperties(gpu, i);
-        GpuDumpQueueProps(p, gpu.inst.surface_extensions, queue_props);
+                // don't print format properties that are unsupported
+                if ((props.linearTilingFeatures || props.optimalTilingFeatures || props.bufferFeatures) == 0) continue;
+
+                GpuDumpFormatProperty(p, fmt, props);
+            }
+        }
     }
-    if (p.Type() == OutputType::json) {
-        p.ArrayEnd();
-    } else {
-        p.ObjectEnd();
+}
+// Print gpu info for text, html, & vkconfig_output
+// Uses a seperate function than schema-json for clarity
+void DumpGpu(Printer &p, AppGpu &gpu, bool show_formats) {
+    ObjectWrapper obj(p, "GPU" + std::to_string(gpu.id));
+    p.IndentDecrease();
+
+    GpuDumpProps(p, gpu);
+    DumpExtensions(p, "Device", gpu.device_extensions);
+    p.AddNewline();
+    {
+        p.SetHeader();
+        ObjectWrapper obj(p, "VkQueueFamilyProperties");
+        for (uint32_t i = 0; i < gpu.queue_count; i++) {
+            AppQueueFamilyProperties queue_props = AppQueueFamilyProperties(gpu, i);
+            GpuDumpQueueProps(p, gpu.inst.surface_extensions, queue_props);
+        }
     }
     GpuDumpMemoryProps(p, gpu);
     GpuDumpFeatures(p, gpu);
-
-    if (p.Type() != OutputType::json) GpuDumpToolingInfo(p, gpu);
+    GpuDumpToolingInfo(p, gpu);
 
     if (p.Type() != OutputType::text || show_formats) {
         GpuDevDump(p, gpu);
     }
 
-    if (p.Type() != OutputType::json) {
-        p.IndentIncrease();
-        p.ObjectEnd();
-    }
+    p.IndentIncrease();
     p.AddNewline();
 }
 
+// Print gpu info for json
+void DumpGpuJson(Printer &p, AppGpu &gpu) {
+    GpuDumpPropsJson(p, gpu);
+    {
+        ArrayWrapper arr(p, "ArrayOfVkQueueFamilyProperties");
+        for (uint32_t i = 0; i < gpu.queue_count; i++) {
+            AppQueueFamilyProperties queue_props = AppQueueFamilyProperties(gpu, i);
+            GpuDumpQueuePropsJson(p, gpu.inst.surface_extensions, queue_props);
+        }
+    }
+    GpuDumpMemoryPropsJson(p, gpu);
+    DumpVkPhysicalDeviceFeatures(p, "VkPhysicalDeviceFeatures", gpu.features);
+    GpuDevDumpJson(p, gpu);
+}
+
 // ============ Printing Logic ============= //
 
 #ifdef _WIN32
@@ -738,10 +779,18 @@ int main(int argc, char **argv) {
 
     uint32_t selected_gpu = 0;
     bool show_formats = false;
+    char *output_path = nullptr;
 
     // Combinations of output: html only, html AND json, json only, human readable only
     for (int i = 1; i < argc; ++i) {
-        if (strncmp("--json", argv[i], 6) == 0 || strcmp(argv[i], "-j") == 0) {
+        // A internal-use-only format for communication with the Vulkan Configurator tool
+        // Usage "--vkconfig_output <path>"
+        if (0 == strcmp("--vkconfig_output", argv[i]) && argc > (i + 1)) {
+            human_readable_output = false;
+            vkconfig_output = true;
+            output_path = argv[i + 1];
+            ++i;
+        } else if (strncmp("--json", argv[i], 6) == 0 || strcmp(argv[i], "-j") == 0) {
             if (strlen(argv[i]) > 7 && strncmp("--json=", argv[i], 7) == 0) {
                 selected_gpu = static_cast<uint32_t>(strtol(argv[i] + 7, nullptr, 10));
             }
@@ -799,6 +848,7 @@ int main(int argc, char **argv) {
     buf = std::cout.rdbuf();
     std::ostream out(buf);
     std::ofstream html_out;
+    std::ofstream vkconfig_out;
 
     if (human_readable_output) {
         printers.push_back(std::unique_ptr<Printer>(new Printer(OutputType::text, out, selected_gpu, instance.vk_version)));
@@ -810,36 +860,45 @@ int main(int argc, char **argv) {
     if (json_output) {
         printers.push_back(std::unique_ptr<Printer>(new Printer(OutputType::json, out, selected_gpu, instance.vk_version)));
     }
+    if (vkconfig_output) {
+#ifdef WIN32
+        vkconfig_out = std::ofstream(std::string(output_path) + "\\vulkaninfo.json");
+#else
+        vkconfig_out = std::ofstream(std::string(output_path) + "/vulkaninfo.json");
+#endif
+        printers.push_back(
+            std::unique_ptr<Printer>(new Printer(OutputType::vkconfig_output, vkconfig_out, selected_gpu, instance.vk_version)));
+    }
 
     for (auto &p : printers) {
-        if (p->Type() != OutputType::json) {
+        if (p->Type() == OutputType::json) {
+            DumpLayers(*p.get(), instance.global_layers, gpus);
+            DumpGpuJson(*p.get(), *gpus.at(selected_gpu).get());
+
+        } else {
             p->SetHeader();
             DumpExtensions(*p.get(), "Instance", instance.global_extensions);
             p->AddNewline();
-        }
 
-        DumpLayers(*p.get(), instance.global_layers, gpus);
+            DumpLayers(*p.get(), instance.global_layers, gpus);
 
-        if (p->Type() != OutputType::json) {
 #if defined(VK_USE_PLATFORM_XCB_KHR) || defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_WIN32_KHR) || \
     defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT) || defined(VK_USE_PLATFORM_WAYLAND_KHR)
             DumpPresentableSurfaces(*p.get(), instance, gpus, surfaces);
 #endif
             DumpGroups(*p.get(), instance);
 
-            p->SetHeader().ObjectStart("Device Properties and Extensions");
+            p->SetHeader();
+            ObjectWrapper obj(*p, "Device Properties and Extensions");
             p->IndentDecrease();
-        }
-        for (auto &gpu : gpus) {
-            if ((p->Type() == OutputType::json && gpu->id == selected_gpu) || p->Type() == OutputType::text ||
-                p->Type() == OutputType::html) {
+
+            for (auto &gpu : gpus) {
                 DumpGpu(*p.get(), *gpu.get(), show_formats);
             }
-        }
-        if (p->Type() != OutputType::json) {
+
             p->IndentIncrease();
-            p->ObjectEnd();
         }
+        p.reset();
     }
 
 #if defined(VK_USE_PLATFORM_XCB_KHR) || defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_WIN32_KHR) || \