Test VIEW_INDEX_FROM_DEVICE_INDEX_BIT with GPL

Add testing of VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT
with graphics pipeline libraries where this flag is set for both
pre-rasterization and fragment pipelines.

VK-GL-CTS issue: 4591

Components: Vulkan

Affects:
dEQP-VK.pipeline.pipeline_library.graphics_library.misc.other.*

Change-Id: I80ccdbeab46103c3ed28fb71a55be2cf96a008e1
diff --git a/android/cts/main/vk-main-2024-03-01/pipeline/pipeline-library.txt b/android/cts/main/vk-main-2024-03-01/pipeline/pipeline-library.txt
index 2e5330c..565a2bf 100644
--- a/android/cts/main/vk-main-2024-03-01/pipeline/pipeline-library.txt
+++ b/android/cts/main/vk-main-2024-03-01/pipeline/pipeline-library.txt
@@ -10428,6 +10428,8 @@
 dEQP-VK.pipeline.pipeline_library.graphics_library.fast.maintenance5
 dEQP-VK.pipeline.pipeline_library.graphics_library.misc.other.common_frag_pipeline_library
 dEQP-VK.pipeline.pipeline_library.graphics_library.misc.other.null_rendering_create_info
+dEQP-VK.pipeline.pipeline_library.graphics_library.misc.other.view_index_from_device_index_in_all_stages
+dEQP-VK.pipeline.pipeline_library.graphics_library.misc.other.view_index_from_device_index_in_mesh_stages
 dEQP-VK.pipeline.pipeline_library.image_2d_view_3d_image.fragment.combined_image_sampler.mip0_layer0_sparse
 dEQP-VK.pipeline.pipeline_library.image_2d_view_3d_image.fragment.combined_image_sampler.mip0_layer63_sparse
 dEQP-VK.pipeline.pipeline_library.image_2d_view_3d_image.fragment.combined_image_sampler.mip2_layer0_sparse
diff --git a/external/vulkancts/modules/vulkan/pipeline/vktPipelineLibraryTests.cpp b/external/vulkancts/modules/vulkan/pipeline/vktPipelineLibraryTests.cpp
index 5fb2e86..c306bf0 100644
--- a/external/vulkancts/modules/vulkan/pipeline/vktPipelineLibraryTests.cpp
+++ b/external/vulkancts/modules/vulkan/pipeline/vktPipelineLibraryTests.cpp
@@ -39,6 +39,7 @@
 #include "vkRayTracingUtil.hpp"
 #include "vktTestCase.hpp"
 #include "vktTestGroupUtil.hpp"
+#include "vktCustomInstancesDevices.hpp"
 #include "tcuCommandLine.hpp"
 #include "tcuImageCompare.hpp"
 #include "tcuTestLog.hpp"
@@ -1347,6 +1348,8 @@
     SHADER_MODULE_CREATE_INFO_RT_LIB,
     NULL_RENDERING_CREATE_INFO,
     COMMON_FRAG_LIBRARY,
+    VIEW_INDEX_FROM_DEVICE_INDEX_IN_ALL_STAGES,
+    VIEW_INDEX_FROM_DEVICE_INDEX_IN_MESH_STAGES,
 };
 
 struct MiscTestParams
@@ -2905,9 +2908,7 @@
     NullRenderingCreateInfoInstance(Context &context) : vkt::TestInstance(context)
     {
     }
-    virtual ~NullRenderingCreateInfoInstance(void)
-    {
-    }
+    virtual ~NullRenderingCreateInfoInstance(void) = default;
 
     tcu::TestStatus iterate(void) override;
 };
@@ -3165,6 +3166,399 @@
     return tcu::TestStatus::pass("Pass");
 }
 
+class CreateViewIndexFromDeviceIndexInstance : public vkt::TestInstance
+{
+public:
+    CreateViewIndexFromDeviceIndexInstance(Context &context, const MiscTestParams &params);
+    virtual ~CreateViewIndexFromDeviceIndexInstance(void) = default;
+
+    tcu::TestStatus iterate(void) override;
+
+protected:
+    bool createDeviceGroup(void);
+
+private:
+    MiscTestParams m_testParams;
+
+    uint32_t m_deviceGroupQueueFamilyIndex;
+    CustomInstance m_deviceGroupInstance;
+    vk::Move<vk::VkDevice> m_deviceGroupLogicalDevice;
+    std::vector<vk::VkPhysicalDevice> m_deviceGroupPhysicalDevices;
+    de::MovePtr<vk::DeviceDriver> m_deviceGroupVk;
+    de::MovePtr<Allocator> m_deviceGroupAllocator;
+};
+
+CreateViewIndexFromDeviceIndexInstance::CreateViewIndexFromDeviceIndexInstance(Context &context,
+                                                                               const MiscTestParams &params)
+    : vkt::TestInstance(context)
+    , m_testParams(params)
+{
+}
+
+tcu::TestStatus CreateViewIndexFromDeviceIndexInstance::iterate()
+{
+    const bool useDeviceGroup = createDeviceGroup();
+    const auto &vk            = useDeviceGroup ? *m_deviceGroupVk : m_context.getDeviceInterface();
+    const auto device         = useDeviceGroup ? *m_deviceGroupLogicalDevice : m_context.getDevice();
+    Allocator &allocator      = useDeviceGroup ? *m_deviceGroupAllocator : m_context.getDefaultAllocator();
+    uint32_t queueFamilyIndex =
+        useDeviceGroup ? m_deviceGroupQueueFamilyIndex : m_context.getUniversalQueueFamilyIndex();
+    const auto deviceCount = useDeviceGroup ? m_deviceGroupPhysicalDevices.size() : 1u;
+    const auto viewCount   = 3u;
+    const auto imageSize   = 8u;
+    const auto colorFormat = VK_FORMAT_R8G8B8A8_UINT;
+    const auto extent      = makeExtent3D(imageSize, imageSize, 1u);
+
+    bool useMeshShader = (m_testParams.mode == MiscTestMode::VIEW_INDEX_FROM_DEVICE_INDEX_IN_MESH_STAGES);
+    VkPipelineCreateFlags preRasterPipelineFlags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR;
+    VkPipelineCreateFlags fragmentPipelineFlags  = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR;
+    preRasterPipelineFlags |= VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT;
+    fragmentPipelineFlags |= VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT;
+
+    // fill structures that are needed for pipeline creation
+    const VkPipelineVertexInputStateCreateInfo vertexInputStateInfo     = initVulkanStructure();
+    VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateInfo       = initVulkanStructure();
+    inputAssemblyStateInfo.topology                                     = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+    const VkPipelineRasterizationStateCreateInfo rasterizationStateInfo = initVulkanStructure();
+
+    const auto viewport                                 = makeViewport(extent);
+    const auto scissor                                  = makeRect2D(extent);
+    VkPipelineViewportStateCreateInfo viewportStateInfo = initVulkanStructure();
+    viewportStateInfo.viewportCount                     = 1u;
+    viewportStateInfo.pViewports                        = &viewport;
+    viewportStateInfo.scissorCount                      = 1u;
+    viewportStateInfo.pScissors                         = &scissor;
+
+    VkPipelineMultisampleStateCreateInfo multisampleStateInfo         = initVulkanStructure();
+    multisampleStateInfo.rasterizationSamples                         = VK_SAMPLE_COUNT_1_BIT;
+    const VkPipelineDepthStencilStateCreateInfo depthStencilStateInfo = initVulkanStructure();
+
+    VkPipelineColorBlendAttachmentState colorBlendAttachmentState;
+    deMemset(&colorBlendAttachmentState, 0x00, sizeof(VkPipelineColorBlendAttachmentState));
+    colorBlendAttachmentState.colorWriteMask = 0xFu;
+    VkPipelineColorBlendAttachmentState colorBlendAttachmentStates[viewCount];
+    std::fill(colorBlendAttachmentStates, colorBlendAttachmentStates + viewCount, colorBlendAttachmentState);
+
+    VkPipelineColorBlendStateCreateInfo colorBlendStateInfo = initVulkanStructure();
+    colorBlendStateInfo.attachmentCount                     = 1;
+    colorBlendStateInfo.pAttachments                        = colorBlendAttachmentStates;
+
+    VkPipelineTessellationStateCreateInfo tessellationStateInfo = initVulkanStructure();
+    tessellationStateInfo.patchControlPoints                    = 3u;
+
+    // create color attachment with required number of layers
+    const auto imageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, viewCount);
+    const VkImageUsageFlags imageUsage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
+    ImageWithBuffer imageWithBuffer(vk, device, allocator, extent, colorFormat, imageUsage, VK_IMAGE_TYPE_2D,
+                                    imageSubresourceRange, viewCount);
+    Move<VkImageView> imageView = makeImageView(vk, device, imageWithBuffer.getImage(), VK_IMAGE_VIEW_TYPE_2D_ARRAY,
+                                                colorFormat, imageSubresourceRange);
+
+    const auto &multiviewFeatures(m_context.getMultiviewFeatures());
+    const auto srl(makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, viewCount));
+    const auto copyRegion(makeBufferImageCopy(extent, srl));
+    const auto beforeCopyBarrier(makeMemoryBarrier(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT));
+    const auto clearValue(makeClearValueColor(tcu::Vec4(0.0f)));
+
+    const auto pipelineLayout(makePipelineLayout(vk, device));
+    auto &bc(m_context.getBinaryCollection());
+    uint32_t preRasterShaderStages = 1;
+    Move<VkShaderModule> preRasterModules[4];
+    VkPipelineShaderStageCreateInfo preRasterShaderInfos[4];
+
+    if (useMeshShader)
+    {
+        preRasterModules[0]     = createShaderModule(vk, device, bc.get("mesh"));
+        preRasterShaderInfos[0] = makePipelineShaderStageCreateInfo(VK_SHADER_STAGE_MESH_BIT_EXT, *preRasterModules[0]);
+    }
+    else
+    {
+        std::string moduleNames[4]{"vert", "tesc", "tese", "geom"};
+        VkShaderStageFlagBits shaderStages[4]{VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
+                                              VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
+                                              VK_SHADER_STAGE_GEOMETRY_BIT};
+
+        if (multiviewFeatures.multiviewTessellationShader)
+        {
+            inputAssemblyStateInfo.topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
+            preRasterShaderStages += 2;
+        }
+
+        if (multiviewFeatures.multiviewGeometryShader)
+        {
+            moduleNames[preRasterShaderStages]  = "geom";
+            shaderStages[preRasterShaderStages] = VK_SHADER_STAGE_GEOMETRY_BIT;
+            ++preRasterShaderStages;
+        }
+
+        for (uint32_t i = 0u; i < preRasterShaderStages; ++i)
+        {
+            preRasterModules[i]     = createShaderModule(vk, device, bc.get(moduleNames[i]));
+            preRasterShaderInfos[i] = makePipelineShaderStageCreateInfo(shaderStages[i], *preRasterModules[i]);
+        }
+    }
+    const auto fragModule(createShaderModule(vk, device, bc.get("frag")));
+    const auto fragShaderInfo(makePipelineShaderStageCreateInfo(VK_SHADER_STAGE_FRAGMENT_BIT, *fragModule));
+
+    // create renderpass and framebuffer
+    uint32_t viewMask        = 0u;
+    uint32_t correlationMask = 0u;
+    for (uint32_t i = 0u; i < viewCount; ++i)
+    {
+        viewMask |= (1 << i);
+        correlationMask |= (1 << i);
+    }
+    VkRenderPassMultiviewCreateInfo multiviewInfo = initVulkanStructure();
+    multiviewInfo.subpassCount                    = 1u;
+    multiviewInfo.pViewMasks                      = &viewMask;
+    multiviewInfo.correlationMaskCount            = 1u;
+    multiviewInfo.pCorrelationMasks               = &correlationMask;
+
+    auto renderPass = makeRenderPass(
+        vk, device, colorFormat, VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_CLEAR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+        VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+        VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 0, &multiviewInfo);
+
+    const auto framebufferCreateInfo = makeFramebufferCreateInfo(*renderPass, 1u, &*imageView, imageSize, imageSize);
+    auto framebuffer                 = createFramebuffer(vk, device, &framebufferCreateInfo);
+
+    // create pre-raster pipeline part
+    VkGraphicsPipelineLibraryCreateInfoEXT preRasterLibraryInfo = initVulkanStructure();
+    preRasterLibraryInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT |
+                                 VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT;
+    VkGraphicsPipelineCreateInfo preRasterPipelineInfo = initVulkanStructure(&preRasterLibraryInfo);
+    preRasterPipelineInfo.flags                        = preRasterPipelineFlags;
+    preRasterPipelineInfo.layout                       = *pipelineLayout;
+    preRasterPipelineInfo.renderPass                   = *renderPass;
+    preRasterPipelineInfo.pVertexInputState            = &vertexInputStateInfo;
+    preRasterPipelineInfo.pInputAssemblyState          = &inputAssemblyStateInfo;
+    preRasterPipelineInfo.pViewportState               = &viewportStateInfo;
+    preRasterPipelineInfo.pRasterizationState          = &rasterizationStateInfo;
+    preRasterPipelineInfo.pTessellationState           = &tessellationStateInfo;
+    preRasterPipelineInfo.stageCount                   = preRasterShaderStages;
+    preRasterPipelineInfo.pStages                      = preRasterShaderInfos;
+    auto preRasterPipelinePart = createGraphicsPipeline(vk, device, DE_NULL, &preRasterPipelineInfo);
+
+    // create fragment pipeline part
+    VkGraphicsPipelineLibraryCreateInfoEXT fragShaderLibInfo = initVulkanStructure();
+    fragShaderLibInfo.flags                                  = VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT |
+                              VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT;
+    VkGraphicsPipelineCreateInfo fragmentPipelineInfo = initVulkanStructure(&fragShaderLibInfo);
+    fragmentPipelineInfo.flags                        = fragmentPipelineFlags;
+    fragmentPipelineInfo.layout                       = pipelineLayout.get();
+    fragmentPipelineInfo.renderPass                   = *renderPass;
+    fragmentPipelineInfo.pMultisampleState            = &multisampleStateInfo;
+    fragmentPipelineInfo.pDepthStencilState           = &depthStencilStateInfo;
+    fragmentPipelineInfo.pColorBlendState             = &colorBlendStateInfo;
+    fragmentPipelineInfo.stageCount                   = 1u;
+    fragmentPipelineInfo.pStages                      = &fragShaderInfo;
+    auto fragmentPipelinePart = createGraphicsPipeline(vk, device, DE_NULL, &fragmentPipelineInfo);
+
+    // merge pipelines
+    const VkPipeline libraryHandles[]{
+        *preRasterPipelinePart,
+        *fragmentPipelinePart,
+    };
+    VkPipelineLibraryCreateInfoKHR linkedPipelineLibraryInfo = initVulkanStructure();
+    linkedPipelineLibraryInfo.libraryCount                   = 2u;
+    linkedPipelineLibraryInfo.pLibraries                     = libraryHandles;
+    VkGraphicsPipelineCreateInfo linkedPipelineInfo          = initVulkanStructure(&linkedPipelineLibraryInfo);
+    linkedPipelineInfo.layout                                = *pipelineLayout;
+    const auto pipeline = createGraphicsPipeline(vk, device, DE_NULL, &linkedPipelineInfo);
+
+    const auto cmdPool(
+        createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
+    const auto cmdBuffer(allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
+
+    // render triangle that covers whole color attachments
+    beginCommandBuffer(vk, *cmdBuffer);
+
+    beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, scissor, 1, &clearValue);
+    vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+
+    if (useMeshShader)
+        vk.cmdDrawMeshTasksEXT(*cmdBuffer, 1u, 1u, 1u);
+    else
+        vk.cmdDraw(*cmdBuffer, 3u, 1u, 0u, 0u);
+
+    endRenderPass(vk, *cmdBuffer);
+
+    vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 1,
+                          &beforeCopyBarrier, 0, 0, 0, 0);
+
+    vk.cmdCopyImageToBuffer(*cmdBuffer, imageWithBuffer.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                            imageWithBuffer.getBuffer(), 1u, &copyRegion);
+
+    endCommandBuffer(vk, *cmdBuffer);
+    const VkQueue queue = getDeviceQueue(vk, device, queueFamilyIndex, 0);
+    submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+    const auto &bufferAllocation = imageWithBuffer.getBufferAllocation();
+    invalidateAlloc(vk, device, bufferAllocation);
+
+    bool resurtIsCorrect      = true;
+    const auto fragmentCount  = imageSize * imageSize;
+    const auto componentCount = 4u;
+    std::vector<uint8_t> allowedValueSets(deviceCount * componentCount, 0);
+
+    for (uint8_t v = 0; v < static_cast<uint8_t>(viewCount); ++v)
+    {
+        // calculate allowed set of result values
+        for (uint8_t d = 0; d < static_cast<uint8_t>(deviceCount); ++d)
+        {
+            uint8_t *allowedValuesPtr = allowedValueSets.data() + d * componentCount;
+            allowedValuesPtr[0]       = d;
+            allowedValuesPtr[1]       = static_cast<uint8_t>(d + d);
+            allowedValuesPtr[2]       = d;
+            allowedValuesPtr[3]       = d;
+
+            // ignore tesselation and/or geometry stages when those features are not available
+            if (!multiviewFeatures.multiviewTessellationShader || useMeshShader)
+                allowedValuesPtr[1] = 0;
+            if (!multiviewFeatures.multiviewGeometryShader || useMeshShader)
+                allowedValuesPtr[2] = 0;
+        }
+
+        uint8_t *bufferPtr = static_cast<uint8_t *>(bufferAllocation.getHostPtr()) + v * fragmentCount * componentCount;
+        for (uint32_t f = 0; f < fragmentCount; ++f)
+        {
+            uint8_t *fragmentColor = bufferPtr + f * componentCount;
+            resurtIsCorrect        = false;
+
+            // compare with all accepted values (if device group is used each device will produce different  result)
+            for (uint8_t d = 0; d < static_cast<uint8_t>(deviceCount); ++d)
+            {
+                uint8_t *allowedValuesPtr = allowedValueSets.data() + d * componentCount;
+                resurtIsCorrect           = (deMemCmp(fragmentColor, allowedValuesPtr, componentCount) == 0);
+
+                // when fragment is corret we can skip checking other allowed values
+                if (resurtIsCorrect)
+                    break;
+            }
+
+            // when fragment is not corret we can skip checking other fragments
+            if (!resurtIsCorrect)
+                break;
+        }
+
+        // when fragment was not corret we can skip checking other views
+        if (!resurtIsCorrect)
+            break;
+    }
+
+    if (resurtIsCorrect)
+        return tcu::TestStatus::pass("Pass");
+
+    // log images
+    auto &log = m_context.getTestContext().getLog();
+    log << tcu::TestLog::ImageSet("Result", "");
+    for (uint32_t v = 0; v < viewCount; ++v)
+    {
+        uint8_t *bufferPtr = static_cast<uint8_t *>(bufferAllocation.getHostPtr()) + v * fragmentCount * componentCount;
+        tcu::PixelBufferAccess resultAccess(mapVkFormat(colorFormat), imageSize, imageSize, 1, bufferPtr);
+        log << tcu::TestLog::Image(std::to_string(v), "", resultAccess);
+    }
+    log << tcu::TestLog::EndImageSet;
+
+    return tcu::TestStatus::fail("Fail");
+}
+
+bool CreateViewIndexFromDeviceIndexInstance::createDeviceGroup(void)
+{
+    const auto &vki                 = m_context.getInstanceInterface();
+    const tcu::CommandLine &cmdLine = m_context.getTestContext().getCommandLine();
+    const uint32_t deviceGroupIndex = cmdLine.getVKDeviceGroupId() - 1;
+    const float queuePriority       = 1.0f;
+
+    // create vulkan instance, list all device groups and select proper one
+    m_deviceGroupInstance         = createCustomInstanceWithExtension(m_context, "VK_KHR_device_group_creation");
+    auto allDeviceGroupProperties = enumeratePhysicalDeviceGroups(vki, m_deviceGroupInstance);
+    auto &devGroupProperties      = allDeviceGroupProperties[deviceGroupIndex];
+    if (devGroupProperties.physicalDeviceCount == 1)
+        return false;
+
+    const InstanceDriver &instance(m_deviceGroupInstance.getDriver());
+    VkPhysicalDeviceFeatures2 deviceFeatures2     = initVulkanStructure();
+    VkDeviceGroupDeviceCreateInfo deviceGroupInfo = initVulkanStructure(&deviceFeatures2);
+    deviceGroupInfo.physicalDeviceCount           = devGroupProperties.physicalDeviceCount;
+    deviceGroupInfo.pPhysicalDevices              = devGroupProperties.physicalDevices;
+
+    uint32_t physicalDeviceIndex = cmdLine.getVKDeviceId() - 1;
+    if (physicalDeviceIndex >= deviceGroupInfo.physicalDeviceCount)
+        physicalDeviceIndex = 0;
+
+    const VkPhysicalDeviceFeatures deviceFeatures =
+        getPhysicalDeviceFeatures(instance, deviceGroupInfo.pPhysicalDevices[physicalDeviceIndex]);
+    deviceFeatures2.features = deviceFeatures;
+    const std::vector<VkQueueFamilyProperties> queueProps =
+        getPhysicalDeviceQueueFamilyProperties(instance, devGroupProperties.physicalDevices[physicalDeviceIndex]);
+
+    VkPhysicalDeviceMultiviewFeatures multiviewFeatures            = m_context.getMultiviewFeatures();
+    VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT gplFeatures = m_context.getGraphicsPipelineLibraryFeaturesEXT();
+    VkPhysicalDeviceMeshShaderFeaturesEXT meshShaderFeatures       = m_context.getMeshShaderFeaturesEXT();
+
+    std::vector<const char *> deviceExtensions{"VK_KHR_pipeline_library", "VK_EXT_graphics_pipeline_library",
+                                               "VK_KHR_multiview"};
+    if (!isCoreDeviceExtension(m_context.getUsedApiVersion(), "VK_KHR_device_group"))
+        deviceExtensions.push_back("VK_KHR_device_group");
+
+    meshShaderFeatures.pNext = nullptr;
+    multiviewFeatures.pNext  = nullptr;
+    gplFeatures.pNext        = &multiviewFeatures;
+    if (m_testParams.mode == MiscTestMode::VIEW_INDEX_FROM_DEVICE_INDEX_IN_MESH_STAGES)
+    {
+        deviceExtensions.push_back("VK_EXT_mesh_shader");
+        multiviewFeatures.pNext = &meshShaderFeatures;
+    }
+    deviceFeatures2.pNext = &gplFeatures;
+
+    m_deviceGroupPhysicalDevices.resize(devGroupProperties.physicalDeviceCount);
+    for (uint32_t pd = 0; pd < devGroupProperties.physicalDeviceCount; pd++)
+        m_deviceGroupPhysicalDevices[pd] = devGroupProperties.physicalDevices[pd];
+
+    for (size_t queueNdx = 0; queueNdx < queueProps.size(); queueNdx++)
+    {
+        if (queueProps[queueNdx].queueFlags & VK_QUEUE_GRAPHICS_BIT)
+            m_deviceGroupQueueFamilyIndex = (uint32_t)queueNdx;
+    }
+
+    VkDeviceQueueCreateInfo queueInfo{
+        VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // VkStructureType sType;
+        DE_NULL,                                    // const void* pNext;
+        (VkDeviceQueueCreateFlags)0u,               // VkDeviceQueueCreateFlags flags;
+        m_deviceGroupQueueFamilyIndex,              // uint32_t queueFamilyIndex;
+        1u,                                         // uint32_t queueCount;
+        &queuePriority                              // const float* pQueuePriorities;
+    };
+
+    const VkDeviceCreateInfo deviceInfo{
+        VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // VkStructureType sType;
+        &deviceGroupInfo,                     // const void* pNext;
+        (VkDeviceCreateFlags)0,               // VkDeviceCreateFlags flags;
+        1u,                                   // uint32_t queueCreateInfoCount;
+        &queueInfo,                           // const VkDeviceQueueCreateInfo* pQueueCreateInfos;
+        0u,                                   // uint32_t enabledLayerCount;
+        DE_NULL,                              // const char* const* ppEnabledLayerNames;
+        uint32_t(deviceExtensions.size()),    // uint32_t enabledExtensionCount;
+        deviceExtensions.data(),              // const char* const* ppEnabledExtensionNames;
+        DE_NULL,                              // const VkPhysicalDeviceFeatures* pEnabledFeatures;
+    };
+
+    m_deviceGroupLogicalDevice = createCustomDevice(m_context.getTestContext().getCommandLine().isValidationEnabled(),
+                                                    m_context.getPlatformInterface(), m_deviceGroupInstance, instance,
+                                                    deviceGroupInfo.pPhysicalDevices[physicalDeviceIndex], &deviceInfo);
+
+    m_deviceGroupVk = de::MovePtr<DeviceDriver>(
+        new DeviceDriver(m_context.getPlatformInterface(), m_deviceGroupInstance, *m_deviceGroupLogicalDevice,
+                         m_context.getUsedApiVersion(), m_context.getTestContext().getCommandLine()));
+
+    m_deviceGroupAllocator = de::MovePtr<Allocator>(
+        new SimpleAllocator(*m_deviceGroupVk, *m_deviceGroupLogicalDevice,
+                            getPhysicalDeviceMemoryProperties(instance, m_deviceGroupPhysicalDevices[0])));
+
+    return true;
+}
+
 class PipelineLibraryMiscTestCase : public TestCase
 {
 public:
@@ -3218,6 +3612,17 @@
             ((numClipDistances + numCullDistances) > limits.maxCombinedClipAndCullDistances))
             TCU_THROW(NotSupportedError, "Specified values of clip or cull distances are not supported");
     }
+
+    if (m_testParams.mode == MiscTestMode::VIEW_INDEX_FROM_DEVICE_INDEX_IN_ALL_STAGES)
+        context.requireDeviceFunctionality("VK_KHR_multiview");
+    else if (m_testParams.mode == MiscTestMode::VIEW_INDEX_FROM_DEVICE_INDEX_IN_MESH_STAGES)
+    {
+        context.requireDeviceFunctionality("VK_KHR_multiview");
+        context.requireDeviceFunctionality("VK_EXT_mesh_shader");
+        const auto &meshShaderFeatures = context.getMeshShaderFeaturesEXT();
+        if (!meshShaderFeatures.multiviewMeshShader)
+            TCU_THROW(NotSupportedError, "multiviewMeshShader not supported");
+    }
 }
 
 void PipelineLibraryMiscTestCase::initPrograms(SourceCollections &programCollection) const
@@ -3535,6 +3940,117 @@
         }
         programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
     }
+    else if (m_testParams.mode == MiscTestMode::VIEW_INDEX_FROM_DEVICE_INDEX_IN_ALL_STAGES)
+    {
+        std::string vert("#version 460\n"
+                         "#extension GL_EXT_multiview : require\n"
+                         "layout(location = 0) flat out uvec4 vViewIndex;"
+                         "void main() {\n"
+                         "  const float x = -1.0 + 4.0 * ((gl_VertexIndex & 2)>>1);\n"
+                         "  const float y = -1.0 + 4.0 * (gl_VertexIndex % 2);\n"
+                         "  gl_Position = vec4(x, y, 0.0, 1.0);\n"
+                         "  vViewIndex = uvec4(0);\n"
+                         "  vViewIndex.x = gl_ViewIndex;\n"
+                         "}\n");
+        programCollection.glslSources.add("vert") << glu::VertexSource(vert);
+
+        std::string tesc("#version 450\n"
+                         "#extension GL_EXT_multiview : require\n"
+                         "layout (vertices = 3) out;\n"
+                         "layout(location = 0) flat in uvec4 vViewIndex[];\n"
+                         "layout(location = 0) flat out uvec4 vtcViewIndex[];\n"
+                         "void main (void)\n"
+                         "{\n"
+                         "  gl_TessLevelInner[0] = 1.0;\n"
+                         "  gl_TessLevelInner[1] = 1.0;\n"
+                         "  gl_TessLevelOuter[0] = 1.0;\n"
+                         "  gl_TessLevelOuter[1] = 1.0;\n"
+                         "  gl_TessLevelOuter[2] = 1.0;\n"
+                         "  gl_TessLevelOuter[3] = 1.0;\n"
+                         "  vtcViewIndex[gl_InvocationID] = vViewIndex[gl_InvocationID];\n"
+                         "  vtcViewIndex[gl_InvocationID].y = gl_ViewIndex;\n"
+                         "  gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
+                         "}\n");
+        programCollection.glslSources.add("tesc") << glu::TessellationControlSource(tesc);
+
+        std::string tese("#version 450\n"
+                         "#extension GL_EXT_multiview : require\n"
+                         "layout(triangles, fractional_odd_spacing, cw) in;\n"
+                         "layout(location = 0) flat in uvec4 vtcViewIndex[];\n"
+                         "layout(location = 0) flat out uvec4 vtViewIndex;\n"
+                         "void main (void)\n"
+                         "{\n"
+                         "  gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) +\n"
+                         "                (gl_TessCoord.y * gl_in[1].gl_Position) +\n"
+                         "                (gl_TessCoord.z * gl_in[2].gl_Position);\n"
+                         "  vtViewIndex = vtcViewIndex[0];\n"
+                         "  vtViewIndex.y += gl_ViewIndex;\n"
+                         "}\n");
+        programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(tese);
+
+        std::string geom("#version 450\n"
+                         "#extension GL_EXT_multiview : require\n"
+                         "layout (triangles) in;\n"
+                         "layout (triangle_strip, max_vertices=3) out;\n"
+                         "layout(location = 0) flat in uvec4 vtViewIndex[];\n"
+                         "layout(location = 0) flat out uvec4 vtgViewIndex;\n"
+                         "void main (void)\n"
+                         "{\n"
+                         "  for (int i = 0; i < 3; i++)\n"
+                         "  {\n"
+                         "    gl_Position = gl_in[i].gl_Position;\n"
+                         "    vtgViewIndex = vtViewIndex[i];\n"
+                         "    vtgViewIndex.z = gl_ViewIndex;\n"
+                         "    EmitVertex();\n"
+                         "  }\n"
+                         "}\n");
+        programCollection.glslSources.add("geom") << glu::GeometrySource(geom);
+
+        std::string frag("#version 460\n"
+                         "#extension GL_EXT_multiview : require\n"
+                         "layout(location = 0) flat in uvec4 vtgViewIndex;\n"
+                         "layout (location=0) out uvec4 color;\n"
+                         "void main () {\n"
+                         "  color = vtgViewIndex;\n"
+                         "  color.a = gl_ViewIndex;\n"
+                         "}\n");
+        programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
+    }
+    else if (m_testParams.mode == MiscTestMode::VIEW_INDEX_FROM_DEVICE_INDEX_IN_MESH_STAGES)
+    {
+        const auto buildOptions =
+            vk::ShaderBuildOptions(programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, 0, true);
+
+        std::string mesh("#version 450\n"
+                         "#extension GL_EXT_mesh_shader : enable\n"
+                         "#extension GL_EXT_multiview : require\n"
+                         "layout(local_size_x=3) in;\n"
+                         "layout(triangles) out;\n"
+                         "layout(max_vertices=3, max_primitives=1) out;\n"
+                         "layout(location = 0) perprimitiveEXT flat out uvec4 mViewIndex[];\n"
+                         "void main() {\n"
+                         "  SetMeshOutputsEXT(3u, 1u);\n"
+                         "  const uint idx = gl_LocalInvocationIndex;\n"
+                         "  const float x = -1.0 + 4.0 * ((idx & 2)>>1);\n"
+                         "  const float y = -1.0 + 4.0 * (idx % 2);\n"
+                         "  gl_MeshVerticesEXT[idx].gl_Position = vec4(x, y, 0.0, 1.0);\n"
+                         "  gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0, 1, 2);\n"
+                         "  mViewIndex[idx] = uvec4(0);\n"
+                         "  mViewIndex[idx].x = gl_ViewIndex;\n"
+                         "}\n");
+        programCollection.glslSources.add("mesh") << glu::MeshSource(mesh) << buildOptions;
+
+        std::string frag("#version 460\n"
+                         "#extension GL_EXT_multiview : require\n"
+                         "#extension GL_EXT_mesh_shader : enable\n"
+                         "layout(location = 0) perprimitiveEXT flat in uvec4 mViewIndex;\n"
+                         "layout (location=0) out uvec4 color;\n"
+                         "void main () {\n"
+                         "  color = mViewIndex;\n"
+                         "  color.a = gl_ViewIndex;\n"
+                         "}\n");
+        programCollection.glslSources.add("frag") << glu::FragmentSource(frag) << buildOptions;
+    }
     else
     {
         DE_ASSERT(false);
@@ -3555,6 +4071,10 @@
     if (m_testParams.mode == MiscTestMode::NULL_RENDERING_CREATE_INFO)
         return new NullRenderingCreateInfoInstance(context);
 
+    if ((m_testParams.mode == MiscTestMode::VIEW_INDEX_FROM_DEVICE_INDEX_IN_ALL_STAGES) ||
+        (m_testParams.mode == MiscTestMode::VIEW_INDEX_FROM_DEVICE_INDEX_IN_MESH_STAGES))
+        return new CreateViewIndexFromDeviceIndexInstance(context, m_testParams);
+
     return new PipelineLibraryMiscTestInstance(context, m_testParams);
 }
 
@@ -3767,6 +4287,14 @@
                                                              {MiscTestMode::NULL_RENDERING_CREATE_INFO, 0u, 0u}));
         otherTests->addChild(new PipelineLibraryMiscTestCase(testCtx, "common_frag_pipeline_library",
                                                              {MiscTestMode::COMMON_FRAG_LIBRARY, 0u, 0u}));
+
+        otherTests->addChild(
+            new PipelineLibraryMiscTestCase(testCtx, "view_index_from_device_index_in_all_stages",
+                                            {MiscTestMode::VIEW_INDEX_FROM_DEVICE_INDEX_IN_ALL_STAGES, 0u, 0u}));
+        otherTests->addChild(
+            new PipelineLibraryMiscTestCase(testCtx, "view_index_from_device_index_in_mesh_stages",
+                                            {MiscTestMode::VIEW_INDEX_FROM_DEVICE_INDEX_IN_MESH_STAGES, 0u, 0u}));
+
         miscTests->addChild(otherTests.release());
     }
 
diff --git a/external/vulkancts/mustpass/main/vk-default/pipeline/pipeline-library.txt b/external/vulkancts/mustpass/main/vk-default/pipeline/pipeline-library.txt
index c69a788..63169ee 100644
--- a/external/vulkancts/mustpass/main/vk-default/pipeline/pipeline-library.txt
+++ b/external/vulkancts/mustpass/main/vk-default/pipeline/pipeline-library.txt
@@ -35162,6 +35162,8 @@
 dEQP-VK.pipeline.pipeline_library.graphics_library.misc.other.compare_link_times
 dEQP-VK.pipeline.pipeline_library.graphics_library.misc.other.null_descriptor_set_in_monolithic_pipeline
 dEQP-VK.pipeline.pipeline_library.graphics_library.misc.other.null_rendering_create_info
+dEQP-VK.pipeline.pipeline_library.graphics_library.misc.other.view_index_from_device_index_in_all_stages
+dEQP-VK.pipeline.pipeline_library.graphics_library.misc.other.view_index_from_device_index_in_mesh_stages
 dEQP-VK.pipeline.pipeline_library.graphics_library.optimize.0_00_11_11
 dEQP-VK.pipeline.pipeline_library.graphics_library.optimize.0_01_11_1
 dEQP-VK.pipeline.pipeline_library.graphics_library.optimize.0_1111