Improve tests for minSampleShading
authorSlawomir Cygan <slawomir.cygan@intel.com>
Mon, 17 Jul 2017 17:27:42 +0000 (19:27 +0200)
committerAlexander Galazin <Alexander.Galazin@arm.com>
Mon, 28 Aug 2017 07:40:58 +0000 (03:40 -0400)
Existing tests had chicken & egg problem: they were just comparing resolved
images rendered to with minSampleShading > 0 to resolved images rendered to with
minSampleShading == 1. This means that any tests could pass in case of
consistent bug in sample shading.

This change reworks the test do proper validation of number of unique
generated samples:
 - Renderpass is extended with passes for copying per-sample data into
 single sampled images

 - Single sampled images are than cross-checked on CPU: for each pixel,
 the number of covered unique samples accross all images must be not less
 than amoung expected from given minSampleShading value.

Affects:
dEQP-VK.pipeline.multisample.min_sample_shading.*

Components: Vulkan

VK-GL-CTS issue: 579

Change-Id: I24cbf471a499de5db39b028e598a13899114a3e4

external/vulkancts/modules/vulkan/pipeline/vktPipelineMultisampleTests.cpp

index 7a0e980..194e62e 100644 (file)
@@ -76,6 +76,15 @@ enum TestModeBits
 };
 typedef deUint32 TestModeFlags;
 
+enum RenderType
+{
+       // resolve multisample rendering to single sampled image
+       RENDER_TYPE_RESOLVE             = 0u,
+
+       // copy samples to an array of single sampled images
+       RENDER_TYPE_COPY_SAMPLES
+};
+
 void                                                                   initMultisamplePrograms                         (SourceCollections& sources, GeometryType geometryType);
 bool                                                                   isSupportedSampleCount                          (const InstanceInterface& instanceInterface, VkPhysicalDevice physicalDevice, VkSampleCountFlagBits rasterizationSamples);
 bool                                                                   isSupportedDepthStencilFormat           (const InstanceInterface& vki, const VkPhysicalDevice physDevice, const VkFormat format);
@@ -148,6 +157,7 @@ public:
        virtual                                                                         ~MinSampleShadingTest                           (void) {}
 
 protected:
+       virtual void                                                            initPrograms                                            (SourceCollections& programCollection) const;
        virtual TestInstance*                                           createMultisampleTestInstance           (Context&                                                                               context,
                                                                                                                                                                         VkPrimitiveTopology                                                    topology,
                                                                                                                                                                         const std::vector<Vertex4RGBA>&                                vertices,
@@ -234,7 +244,8 @@ public:
                                                                                                                                                         const VkPrimitiveTopology                                              topology,
                                                                                                                                                         const std::vector<Vertex4RGBA>&                                vertices,
                                                                                                                                                         const VkPipelineMultisampleStateCreateInfo&    multisampleStateParams,
-                                                                                                                                                        const VkPipelineColorBlendAttachmentState&             blendState);
+                                                                                                                                                        const VkPipelineColorBlendAttachmentState&             blendState,
+                                                                                                                                                        const RenderType                                                               renderType);
 
                                                                                                MultisampleRenderer                     (Context&                                                                               context,
                                                                                                                                                         const VkFormat                                                                 colorFormat,
@@ -246,11 +257,13 @@ public:
                                                                                                                                                         const VkPrimitiveTopology*                                             pTopology,
                                                                                                                                                         const std::vector<Vertex4RGBA>*                                pVertices,
                                                                                                                                                         const VkPipelineMultisampleStateCreateInfo&    multisampleStateParams,
-                                                                                                                                                        const VkPipelineColorBlendAttachmentState&             blendState);
+                                                                                                                                                        const VkPipelineColorBlendAttachmentState&             blendState,
+                                                                                                                                                    const RenderType                                                           renderType);
 
        virtual                                                                         ~MultisampleRenderer            (void);
 
        de::MovePtr<tcu::TextureLevel>                          render                                          (void);
+       de::MovePtr<tcu::TextureLevel>                          getSingleSampledImage           (deUint32 sampleId);
 
 protected:
        void                                                                            initialize                                      (Context&                                                                               context,
@@ -269,6 +282,8 @@ protected:
        const VkPipelineMultisampleStateCreateInfo      m_multisampleStateParams;
        const VkPipelineColorBlendAttachmentState       m_colorBlendState;
 
+       const RenderType                                                        m_renderType;
+
        Move<VkImage>                                                           m_colorImage;
        de::MovePtr<Allocation>                                         m_colorImageAlloc;
        Move<VkImageView>                                                       m_colorAttachmentView;
@@ -277,6 +292,14 @@ protected:
        de::MovePtr<Allocation>                                         m_resolveImageAlloc;
        Move<VkImageView>                                                       m_resolveAttachmentView;
 
+       struct PerSampleImage
+       {
+               Move<VkImage>                                                           m_image;
+               de::MovePtr<Allocation>                                         m_imageAlloc;
+               Move<VkImageView>                                                       m_attachmentView;
+       };
+       std::vector<de::SharedPtr<PerSampleImage> >     m_perSampleImages;
+
        Move<VkImage>                                                           m_depthStencilImage;
        de::MovePtr<Allocation>                                         m_depthStencilImageAlloc;
        Move<VkImageView>                                                       m_depthStencilAttachmentView;
@@ -287,12 +310,22 @@ protected:
        Move<VkShaderModule>                                            m_vertexShaderModule;
        Move<VkShaderModule>                                            m_fragmentShaderModule;
 
+       Move<VkShaderModule>                                            m_copySampleVertexShaderModule;
+       Move<VkShaderModule>                                            m_copySampleFragmentShaderModule;
+
        Move<VkBuffer>                                                          m_vertexBuffer;
        de::MovePtr<Allocation>                                         m_vertexBufferAlloc;
 
        Move<VkPipelineLayout>                                          m_pipelineLayout;
        std::vector<VkPipelineSp>                                       m_graphicsPipelines;
 
+       Move<VkDescriptorSetLayout>                                     m_copySampleDesciptorLayout;
+       Move<VkDescriptorPool>                                          m_copySampleDesciptorPool;
+       Move<VkDescriptorSet>                                           m_copySampleDesciptorSet;
+
+       Move<VkPipelineLayout>                                          m_copySamplePipelineLayout;
+       std::vector<VkPipelineSp>                                       m_copySamplePipelines;
+
        Move<VkCommandPool>                                                     m_cmdPool;
        Move<VkCommandBuffer>                                           m_cmdBuffer;
 
@@ -337,9 +370,9 @@ public:
        virtual tcu::TestStatus                                         iterate                                         (void);
 
 protected:
-       virtual tcu::TestStatus                                         verifyImage                                     (const tcu::ConstPixelBufferAccess& testShadingImage,
-                                                                                                                                                        const tcu::ConstPixelBufferAccess& minShadingImage,
-                                                                                                                                                        const tcu::ConstPixelBufferAccess& maxShadingImage);
+       virtual tcu::TestStatus                                         verifySampleShadedImage         (const std::vector<tcu::TextureLevel>& testShadingImages,
+                                                                                                                                                        const tcu::ConstPixelBufferAccess& noSampleshadingImage);
+
        const VkFormat                                                          m_colorFormat;
        const tcu::IVec2                                                        m_renderSize;
        const VkPrimitiveTopology                                       m_primitiveTopology;
@@ -436,7 +469,7 @@ void initMultisamplePrograms (SourceCollections& sources, GeometryType geometryT
                "       gl_Position = position;\n"
                "       vtxColor = color;\n"
                << (geometryType == GEOMETRY_TYPE_OPAQUE_POINT ? "      gl_PointSize = 3.0f;\n"
-                                                                                                                : "" )
+                       : "")
                << "}\n";
 
        static const char* fragmentSource =
@@ -452,6 +485,67 @@ void initMultisamplePrograms (SourceCollections& sources, GeometryType geometryT
        sources.glslSources.add("color_frag") << glu::FragmentSource(fragmentSource);
 }
 
+void initSampleShadingPrograms (SourceCollections& sources, GeometryType geometryType)
+{
+       {
+               std::ostringstream vertexSource;
+
+               vertexSource <<
+                       "#version 440\n"
+                       "layout(location = 0) in vec4 position;\n"
+                       "layout(location = 1) in vec4 color;\n"
+                       "void main (void)\n"
+                       "{\n"
+                       "       gl_Position = position;\n"
+                       << (geometryType == GEOMETRY_TYPE_OPAQUE_POINT ? "      gl_PointSize = 3.0f;\n"
+                               : "")
+                       << "}\n";
+
+               static const char* fragmentSource =
+                       "#version 440\n"
+                       "layout(location = 0) out highp vec4 fragColor;\n"
+                       "void main (void)\n"
+                       "{\n"
+                       "       fragColor = vec4(fract(gl_FragCoord.xy), 0.0, 1.0);\n"
+                       "}\n";
+
+               sources.glslSources.add("color_vert") << glu::VertexSource(vertexSource.str());
+               sources.glslSources.add("color_frag") << glu::FragmentSource(fragmentSource);
+       }
+
+       {
+               static const char*  vertexSource =
+                       "#version 440\n"
+                       "void main (void)\n"
+                       "{\n"
+                       "       const vec4 positions[4] = vec4[4](\n"
+                       "               vec4(-1.0, -1.0, 0.0, 1.0),\n"
+                       "               vec4(-1.0,  1.0, 0.0, 1.0),\n"
+                       "               vec4( 1.0, -1.0, 0.0, 1.0),\n"
+                       "               vec4( 1.0,  1.0, 0.0, 1.0)\n"
+                       "       );\n"
+                       "       gl_Position = positions[gl_VertexIndex];\n"
+                       "}\n";
+
+               static const char* fragmentSource =
+                       "#version 440\n"
+                       "precision highp float;\n"
+                       "layout(location = 0) out highp vec4 fragColor;\n"
+                       "layout(set = 0, binding = 0, input_attachment_index = 0) uniform subpassInputMS imageMS;\n"
+                       "layout(push_constant) uniform PushConstantsBlock\n"
+                       "{\n"
+                       "       int sampleId;\n"
+                       "} pushConstants;\n"
+                       "void main (void)\n"
+                       "{\n"
+                       "       fragColor = subpassLoad(imageMS, pushConstants.sampleId);\n"
+                       "}\n";
+
+               sources.glslSources.add("quad_vert") << glu::VertexSource(vertexSource);
+               sources.glslSources.add("copy_sample_frag") << glu::FragmentSource(fragmentSource);
+       }
+}
+
 bool isSupportedSampleCount (const InstanceInterface& instanceInterface, VkPhysicalDevice physicalDevice, VkSampleCountFlagBits rasterizationSamples)
 {
        VkPhysicalDeviceProperties deviceProperties;
@@ -772,6 +866,11 @@ MinSampleShadingTest::MinSampleShadingTest (tcu::TestContext&              testContext,
 {
 }
 
+void MinSampleShadingTest::initPrograms (SourceCollections& programCollection) const
+{
+       initSampleShadingPrograms(programCollection, m_geometryType);
+}
+
 TestInstance* MinSampleShadingTest::createMultisampleTestInstance (Context&                                                                            context,
                                                                                                                                   VkPrimitiveTopology                                                  topology,
                                                                                                                                   const std::vector<Vertex4RGBA>&                              vertices,
@@ -965,12 +1064,12 @@ RasterizationSamplesInstance::RasterizationSamplesInstance (Context&                                                                             co
 
                m_multisampleRenderer = de::MovePtr<MultisampleRenderer>(
                        new MultisampleRenderer(
-                               context, m_colorFormat, depthStencilFormat, m_renderSize, useDepth, useStencil, 2u, pTopology, pVertices, multisampleStateParams, blendState));
+                               context, m_colorFormat, depthStencilFormat, m_renderSize, useDepth, useStencil, 2u, pTopology, pVertices, multisampleStateParams, blendState, RENDER_TYPE_RESOLVE));
        }
        else
        {
                m_multisampleRenderer = de::MovePtr<MultisampleRenderer>(
-                       new MultisampleRenderer(context, m_colorFormat, m_renderSize, topology, vertices, multisampleStateParams, blendState));
+                       new MultisampleRenderer(context, m_colorFormat, m_renderSize, topology, vertices, multisampleStateParams, blendState, RENDER_TYPE_RESOLVE));
        }
 }
 
@@ -1063,55 +1162,109 @@ MinSampleShadingInstance::MinSampleShadingInstance (Context&                                                                   context,
 
 tcu::TestStatus MinSampleShadingInstance::iterate (void)
 {
-       de::MovePtr<tcu::TextureLevel>                          testShadingImage;
-       de::MovePtr<tcu::TextureLevel>                          minShadingImage;
-       de::MovePtr<tcu::TextureLevel>                          maxShadingImage;
+       de::MovePtr<tcu::TextureLevel>  noSampleshadingImage;
+       std::vector<tcu::TextureLevel>  sampleShadedImages;
 
-       // Render with test minSampleShading
+       // Render and resolve without sample shading
        {
-               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, m_multisampleStateParams, m_colorBlendState);
-               testShadingImage = renderer.render();
+               VkPipelineMultisampleStateCreateInfo multisampleStateParms = m_multisampleStateParams;
+               multisampleStateParms.sampleShadingEnable       = VK_FALSE;
+               multisampleStateParms.minSampleShading          = 0.0;
+
+               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, multisampleStateParms, m_colorBlendState, RENDER_TYPE_RESOLVE);
+               noSampleshadingImage  = renderer.render();
        }
 
-       // Render with minSampleShading = 0.0f
+       // Render with test minSampleShading and collect per-sample images
        {
-               VkPipelineMultisampleStateCreateInfo    multisampleParams       = m_multisampleStateParams;
-               multisampleParams.minSampleShading = 0.0f;
+               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, m_multisampleStateParams, m_colorBlendState, RENDER_TYPE_COPY_SAMPLES);
+               renderer.render();
 
-               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, multisampleParams, m_colorBlendState);
-               minShadingImage = renderer.render();
+               sampleShadedImages.resize(m_multisampleStateParams.rasterizationSamples);
+               for (deUint32 sampleId = 0; sampleId < sampleShadedImages.size(); sampleId++)
+               {
+                       sampleShadedImages[sampleId] = *renderer.getSingleSampledImage(sampleId);
+               }
        }
 
-       // Render with minSampleShading = 1.0f
+       // Log images
        {
-               VkPipelineMultisampleStateCreateInfo    multisampleParams       = m_multisampleStateParams;
-               multisampleParams.minSampleShading = 1.0f;
+               tcu::TestLog& testLog   = m_context.getTestContext().getLog();
 
-               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, multisampleParams, m_colorBlendState);
-               maxShadingImage = renderer.render();
+               testLog << tcu::TestLog::ImageSet("Images", "Images")
+                               << tcu::TestLog::Image("noSampleshadingImage", "Image rendered without sample shading", noSampleshadingImage->getAccess());
+
+               for (deUint32 sampleId = 0; sampleId < sampleShadedImages.size(); sampleId++)
+               {
+                       testLog << tcu::TestLog::Image("sampleShadedImage", "One sample of sample shaded image", sampleShadedImages[sampleId].getAccess());
+               }
+               testLog << tcu::TestLog::EndImageSet;
        }
 
-       return verifyImage(testShadingImage->getAccess(), minShadingImage->getAccess(), maxShadingImage->getAccess());
+       return verifySampleShadedImage(sampleShadedImages, noSampleshadingImage->getAccess());
 }
 
-tcu::TestStatus MinSampleShadingInstance::verifyImage (const tcu::ConstPixelBufferAccess& testShadingImage, const tcu::ConstPixelBufferAccess& minShadingImage, const tcu::ConstPixelBufferAccess& maxShadingImage)
+tcu::TestStatus MinSampleShadingInstance::verifySampleShadedImage (const std::vector<tcu::TextureLevel>& sampleShadedImages, const tcu::ConstPixelBufferAccess& noSampleshadingImage)
 {
-       const deUint32  testColorCount  = getUniqueColorsCount(testShadingImage);
-       const deUint32  minColorCount   = getUniqueColorsCount(minShadingImage);
-       const deUint32  maxColorCount   = getUniqueColorsCount(maxShadingImage);
+       const deUint32  pixelCount      = noSampleshadingImage.getWidth() * noSampleshadingImage.getHeight() * noSampleshadingImage.getDepth();
 
-       tcu::TestLog& log = m_context.getTestContext().getLog();
+       bool anyPixelCovered            = false;
 
-       log << tcu::TestLog::Message
-               << "\nColors found: " << testColorCount << "\n"
-               << "Min. colors expected: " << minColorCount << "\n"
-               << "Max. colors expected: " << maxColorCount << "\n"
-               << tcu::TestLog::EndMessage;
+       for (deUint32 pixelNdx = 0; pixelNdx < pixelCount; pixelNdx++)
+       {
+               const deUint32 noSampleShadingValue = *((const deUint32*)noSampleshadingImage.getDataPtr() + pixelNdx);
 
-       if (minColorCount > testColorCount || testColorCount > maxColorCount)
-               return tcu::TestStatus::fail("Unique colors out of expected bounds");
-       else
-               return tcu::TestStatus::pass("Unique colors within expected bounds");
+               if (noSampleShadingValue == 0)
+               {
+                       // non-covered pixel, continue
+                       continue;
+               }
+               else
+               {
+                       anyPixelCovered = true;
+               }
+
+               int numNotCoveredSamples = 0;
+
+               std::map<deUint32, deUint32>    histogram; // map<pixel value, number of occurrences>
+
+               // Collect histogram of occurrences or each pixel across all samples
+               for (size_t i = 0; i < sampleShadedImages.size(); ++i)
+               {
+                       const deUint32 sampleShadedValue = *((const deUint32*)sampleShadedImages[i].getAccess().getDataPtr() + pixelNdx);
+
+                       if (sampleShadedValue == 0)
+                       {
+                               numNotCoveredSamples++;
+                               continue;
+                       }
+
+                       if (histogram.find(sampleShadedValue) != histogram.end())
+                               histogram[sampleShadedValue]++;
+                       else
+                               histogram[sampleShadedValue] = 1;
+               }
+
+               if (numNotCoveredSamples == static_cast<int>(sampleShadedImages.size()))
+               {
+                       return tcu::TestStatus::fail("Got uncovered pixel, where covered samples were expected");
+               }
+
+               const int uniqueColorsCount                             = (int)histogram.size();
+               const int expectedUniqueSamplesCount    = static_cast<int>(m_multisampleStateParams.minSampleShading * static_cast<float>(sampleShadedImages.size()) + 0.5f);
+
+               if (uniqueColorsCount + numNotCoveredSamples < expectedUniqueSamplesCount)
+               {
+                       return tcu::TestStatus::fail("Got less unique colors than requested through minSampleShading");
+               }
+       }
+
+       if (!anyPixelCovered)
+       {
+               return tcu::TestStatus::fail("Did not get any covered pixel, cannot test minSampleShading");
+       }
+
+       return tcu::TestStatus::pass("Got proper count of unique colors");
 }
 
 SampleMaskInstance::SampleMaskInstance (Context&                                                                               context,
@@ -1137,7 +1290,7 @@ tcu::TestStatus SampleMaskInstance::iterate (void)
 
        // Render with test flags
        {
-               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, m_multisampleStateParams, m_colorBlendState);
+               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, m_multisampleStateParams, m_colorBlendState, RENDER_TYPE_COPY_SAMPLES);
                testSampleMaskImage = renderer.render();
        }
 
@@ -1148,7 +1301,7 @@ tcu::TestStatus SampleMaskInstance::iterate (void)
 
                multisampleParams.pSampleMask = sampleMask.data();
 
-               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, multisampleParams, m_colorBlendState);
+               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, multisampleParams, m_colorBlendState, RENDER_TYPE_COPY_SAMPLES);
                minSampleMaskImage = renderer.render();
        }
 
@@ -1159,7 +1312,7 @@ tcu::TestStatus SampleMaskInstance::iterate (void)
 
                multisampleParams.pSampleMask = sampleMask.data();
 
-               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, multisampleParams, m_colorBlendState);
+               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, multisampleParams, m_colorBlendState, RENDER_TYPE_COPY_SAMPLES);
                maxSampleMaskImage = renderer.render();
        }
 
@@ -1243,7 +1396,7 @@ tcu::TestStatus testRasterSamplesConsistency (Context& context, GeometryType geo
                        false                                                                                                           // VkBool32                                                                     alphaToOneEnable;
                };
 
-               MultisampleRenderer                             renderer                (context, VK_FORMAT_R8G8B8A8_UNORM, tcu::IVec2(32, 32), VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, vertices, multisampleStateParams, getDefaultColorBlendAttachmentState());
+               MultisampleRenderer                             renderer                (context, VK_FORMAT_R8G8B8A8_UNORM, tcu::IVec2(32, 32), VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, vertices, multisampleStateParams, getDefaultColorBlendAttachmentState(), RENDER_TYPE_RESOLVE);
                de::MovePtr<tcu::TextureLevel>  result                  = renderer.render();
                const deUint32                                  uniqueColors    = getUniqueColorsCount(result->getAccess());
 
@@ -1300,7 +1453,7 @@ tcu::TestStatus AlphaToOneInstance::iterate       (void)
 
        // Render with blend enabled and alpha to one on
        {
-               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, m_multisampleStateParams, m_colorBlendState);
+               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, m_multisampleStateParams, m_colorBlendState, RENDER_TYPE_RESOLVE);
                alphaOneImage = renderer.render();
        }
 
@@ -1309,7 +1462,7 @@ tcu::TestStatus AlphaToOneInstance::iterate       (void)
                VkPipelineMultisampleStateCreateInfo    multisampleParams       = m_multisampleStateParams;
                multisampleParams.alphaToOneEnable = false;
 
-               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, multisampleParams, m_colorBlendState);
+               MultisampleRenderer renderer (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, multisampleParams, m_colorBlendState, RENDER_TYPE_RESOLVE);
                noAlphaOneImage = renderer.render();
        }
 
@@ -1360,7 +1513,7 @@ tcu::TestStatus AlphaToCoverageInstance::iterate (void)
        DE_ASSERT(m_multisampleStateParams.alphaToCoverageEnable);
 
        de::MovePtr<tcu::TextureLevel>  result;
-       MultisampleRenderer                             renderer        (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, m_multisampleStateParams, m_colorBlendState);
+       MultisampleRenderer                             renderer        (m_context, m_colorFormat, m_renderSize, m_primitiveTopology, m_vertices, m_multisampleStateParams, m_colorBlendState, RENDER_TYPE_RESOLVE);
 
        result = renderer.render();
 
@@ -1415,7 +1568,8 @@ MultisampleRenderer::MultisampleRenderer (Context&                                                                                context,
                                                                                  const VkPrimitiveTopology                                             topology,
                                                                                  const std::vector<Vertex4RGBA>&                               vertices,
                                                                                  const VkPipelineMultisampleStateCreateInfo&   multisampleStateParams,
-                                                                                 const VkPipelineColorBlendAttachmentState&    blendState)
+                                                                                 const VkPipelineColorBlendAttachmentState&    blendState,
+                                                                                 const RenderType                                                              renderType)
        : m_context                                     (context)
        , m_colorFormat                         (colorFormat)
        , m_depthStencilFormat          (VK_FORMAT_UNDEFINED)
@@ -1424,6 +1578,7 @@ MultisampleRenderer::MultisampleRenderer (Context&                                                                                context,
        , m_useStencil                          (false)
        , m_multisampleStateParams      (multisampleStateParams)
        , m_colorBlendState                     (blendState)
+       , m_renderType                          (renderType)
 {
        initialize(context, 1u, &topology, &vertices);
 }
@@ -1438,7 +1593,8 @@ MultisampleRenderer::MultisampleRenderer (Context&                                                                                context,
                                                                                  const VkPrimitiveTopology*                                    pTopology,
                                                                                  const std::vector<Vertex4RGBA>*                               pVertices,
                                                                                  const VkPipelineMultisampleStateCreateInfo&   multisampleStateParams,
-                                                                                 const VkPipelineColorBlendAttachmentState&    blendState)
+                                                                                 const VkPipelineColorBlendAttachmentState&    blendState,
+                                                                                 const RenderType                                                              renderType)
        : m_context                                     (context)
        , m_colorFormat                         (colorFormat)
        , m_depthStencilFormat          (depthStencilFormat)
@@ -1447,6 +1603,7 @@ MultisampleRenderer::MultisampleRenderer (Context&                                                                                context,
        , m_useStencil                          (useStencil)
        , m_multisampleStateParams      (multisampleStateParams)
        , m_colorBlendState                     (blendState)
+       , m_renderType                          (renderType)
 {
        initialize(context, numTopologies, pTopology, pVertices);
 }
@@ -1467,6 +1624,10 @@ void MultisampleRenderer::initialize (Context&                                                                   context,
 
        // Create color image
        {
+
+               const VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+                       (m_renderType == RENDER_TYPE_COPY_SAMPLES ? VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT : (VkImageUsageFlagBits)0u);
+
                const VkImageCreateInfo colorImageParams =
                {
                        VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,                                                                            // VkStructureType                      sType;
@@ -1479,7 +1640,7 @@ void MultisampleRenderer::initialize (Context&                                                                    context,
                        1u,                                                                                                                                                     // deUint32                                     arrayLayers;
                        m_multisampleStateParams.rasterizationSamples,                                                          // VkSampleCountFlagBits        samples;
                        VK_IMAGE_TILING_OPTIMAL,                                                                                                        // VkImageTiling                        tiling;
-                       VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,          // VkImageUsageFlags            usage;
+                       imageUsageFlags,                                                                                                                        // VkImageUsageFlags            usage;
                        VK_SHARING_MODE_EXCLUSIVE,                                                                                                      // VkSharingMode                        sharingMode;
                        1u,                                                                                                                                                     // deUint32                                     queueFamilyIndexCount;
                        &queueFamilyIndex,                                                                                                                      // const deUint32*                      pQueueFamilyIndices;
@@ -1494,6 +1655,7 @@ void MultisampleRenderer::initialize (Context&                                                                    context,
        }
 
        // Create resolve image
+       if (m_renderType == RENDER_TYPE_RESOLVE)
        {
                const VkImageCreateInfo resolveImageParams =
                {
@@ -1520,6 +1682,78 @@ void MultisampleRenderer::initialize (Context&                                                                   context,
                // Allocate and bind resolve image memory
                m_resolveImageAlloc = memAlloc.allocate(getImageMemoryRequirements(vk, vkDevice, *m_resolveImage), MemoryRequirement::Any);
                VK_CHECK(vk.bindImageMemory(vkDevice, *m_resolveImage, m_resolveImageAlloc->getMemory(), m_resolveImageAlloc->getOffset()));
+
+               // Create resolve attachment view
+               {
+                       const VkImageViewCreateInfo resolveAttachmentViewParams =
+                       {
+                               VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,               // VkStructureType                      sType;
+                               DE_NULL,                                                                                // const void*                          pNext;
+                               0u,                                                                                             // VkImageViewCreateFlags       flags;
+                               *m_resolveImage,                                                                // VkImage                                      image;
+                               VK_IMAGE_VIEW_TYPE_2D,                                                  // VkImageViewType                      viewType;
+                               m_colorFormat,                                                                  // VkFormat                                     format;
+                               componentMappingRGBA,                                                   // VkComponentMapping           components;
+                               { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }   // VkImageSubresourceRange      subresourceRange;
+                       };
+
+                       m_resolveAttachmentView = createImageView(vk, vkDevice, &resolveAttachmentViewParams);
+               }
+       }
+
+       // Create per-sample output images
+       if (m_renderType == RENDER_TYPE_COPY_SAMPLES)
+       {
+               const VkImageCreateInfo perSampleImageParams =
+               {
+                       VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,                                                                                    // VkStructureType                      sType;
+                       DE_NULL,                                                                                                                                                // const void*                          pNext;
+                       0u,                                                                                                                                                             // VkImageCreateFlags           flags;
+                       VK_IMAGE_TYPE_2D,                                                                                                                               // VkImageType                          imageType;
+                       m_colorFormat,                                                                                                                                  // VkFormat                                     format;
+                       { (deUint32)m_renderSize.x(), (deUint32)m_renderSize.y(), 1u },                                 // VkExtent3D                           extent;
+                       1u,                                                                                                                                                             // deUint32                                     mipLevels;
+                       1u,                                                                                                                                                             // deUint32                                     arrayLayers;
+                       VK_SAMPLE_COUNT_1_BIT,                                                                                                                  // VkSampleCountFlagBits        samples;
+                       VK_IMAGE_TILING_OPTIMAL,                                                                                                                // VkImageTiling                        tiling;
+                       VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |                 // VkImageUsageFlags            usage;
+                       VK_IMAGE_USAGE_TRANSFER_DST_BIT,
+                       VK_SHARING_MODE_EXCLUSIVE,                                                                                                              // VkSharingMode                        sharingMode;
+                       1u,                                                                                                                                                             // deUint32                                     queueFamilyIndexCount;
+                       &queueFamilyIndex,                                                                                                                              // const deUint32*                      pQueueFamilyIndices;
+                       VK_IMAGE_LAYOUT_UNDEFINED                                                                                                               // VkImageLayout                        initialLayout;
+               };
+
+               m_perSampleImages.resize(static_cast<size_t>(m_multisampleStateParams.rasterizationSamples));
+
+               for (size_t i = 0; i < m_perSampleImages.size(); ++i)
+               {
+                       m_perSampleImages[i]    = de::SharedPtr<PerSampleImage>(new PerSampleImage);
+                       PerSampleImage& image   = *m_perSampleImages[i];
+
+                       image.m_image                   = createImage(vk, vkDevice, &perSampleImageParams);
+
+                       // Allocate and bind image memory
+                       image.m_imageAlloc              = memAlloc.allocate(getImageMemoryRequirements(vk, vkDevice, *image.m_image), MemoryRequirement::Any);
+                       VK_CHECK(vk.bindImageMemory(vkDevice, *image.m_image, image.m_imageAlloc->getMemory(), image.m_imageAlloc->getOffset()));
+
+                       // Create per-sample attachment view
+                       {
+                               const VkImageViewCreateInfo perSampleAttachmentViewParams =
+                               {
+                                       VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,               // VkStructureType                      sType;
+                                       DE_NULL,                                                                                // const void*                          pNext;
+                                       0u,                                                                                             // VkImageViewCreateFlags       flags;
+                                       *image.m_image,                                                                 // VkImage                                      image;
+                                       VK_IMAGE_VIEW_TYPE_2D,                                                  // VkImageViewType                      viewType;
+                                       m_colorFormat,                                                                  // VkFormat                                     format;
+                                       componentMappingRGBA,                                                   // VkComponentMapping           components;
+                                       { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }   // VkImageSubresourceRange      subresourceRange;
+                               };
+
+                               image.m_attachmentView = createImageView(vk, vkDevice, &perSampleAttachmentViewParams);
+                       }
+               }
        }
 
        // Create a depth/stencil image
@@ -1568,25 +1802,7 @@ void MultisampleRenderer::initialize (Context&                                                                   context,
                m_colorAttachmentView = createImageView(vk, vkDevice, &colorAttachmentViewParams);
        }
 
-       // Create resolve attachment view
-       {
-               const VkImageViewCreateInfo resolveAttachmentViewParams =
-               {
-                       VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,               // VkStructureType                      sType;
-                       DE_NULL,                                                                                // const void*                          pNext;
-                       0u,                                                                                             // VkImageViewCreateFlags       flags;
-                       *m_resolveImage,                                                                // VkImage                                      image;
-                       VK_IMAGE_VIEW_TYPE_2D,                                                  // VkImageViewType                      viewType;
-                       m_colorFormat,                                                                  // VkFormat                                     format;
-                       componentMappingRGBA,                                                   // VkComponentMapping           components;
-                       { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }   // VkImageSubresourceRange      subresourceRange;
-               };
-
-               m_resolveAttachmentView = createImageView(vk, vkDevice, &resolveAttachmentViewParams);
-       }
-
        VkImageAspectFlags      depthStencilAttachmentAspect    = (VkImageAspectFlagBits)0;
-       const deUint32          numUsedAttachments                              = (m_useDepth || m_useStencil ? 3u : 2u);
 
        // Create depth/stencil attachment view
        if (m_useDepth || m_useStencil)
@@ -1610,8 +1826,9 @@ void MultisampleRenderer::initialize (Context&                                                                    context,
 
        // Create render pass
        {
-               const VkAttachmentDescription attachmentDescriptions[3] =
+               std::vector<VkAttachmentDescription> attachmentDescriptions;
                {
+                       const VkAttachmentDescription colorAttachmentDescription =
                        {
                                0u,                                                                                                     // VkAttachmentDescriptionFlags         flags;
                                m_colorFormat,                                                                          // VkFormat                                                     format;
@@ -1622,7 +1839,17 @@ void MultisampleRenderer::initialize (Context&                                                                   context,
                                VK_ATTACHMENT_STORE_OP_DONT_CARE,                                       // VkAttachmentStoreOp                          stencilStoreOp;
                                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,                       // VkImageLayout                                        initialLayout;
                                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL                        // VkImageLayout                                        finalLayout;
-                       },
+                       };
+                       attachmentDescriptions.push_back(colorAttachmentDescription);
+               }
+
+               deUint32 resolveAttachmentIndex = VK_ATTACHMENT_UNUSED;
+
+               if (m_renderType == RENDER_TYPE_RESOLVE)
+               {
+                       resolveAttachmentIndex = static_cast<deUint32>(attachmentDescriptions.size());
+
+                       const VkAttachmentDescription resolveAttachmentDescription =
                        {
                                0u,                                                                                                     // VkAttachmentDescriptionFlags         flags;
                                m_colorFormat,                                                                          // VkFormat                                                     format;
@@ -1633,7 +1860,42 @@ void MultisampleRenderer::initialize (Context&                                                                   context,
                                VK_ATTACHMENT_STORE_OP_DONT_CARE,                                       // VkAttachmentStoreOp                          stencilStoreOp;
                                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,                       // VkImageLayout                                        initialLayout;
                                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL                        // VkImageLayout                                        finalLayout;
-                       },
+                       };
+                       attachmentDescriptions.push_back(resolveAttachmentDescription);
+               }
+
+               deUint32 perSampleAttachmentIndex = VK_ATTACHMENT_UNUSED;
+
+               if (m_renderType == RENDER_TYPE_COPY_SAMPLES)
+               {
+                       perSampleAttachmentIndex = static_cast<deUint32>(attachmentDescriptions.size());
+
+                       const VkAttachmentDescription perSampleAttachmentDescription =
+                       {
+                               0u,                                                                                                     // VkAttachmentDescriptionFlags         flags;
+                               m_colorFormat,                                                                          // VkFormat                                                     format;
+                               VK_SAMPLE_COUNT_1_BIT,                                                          // VkSampleCountFlagBits                        samples;
+                               VK_ATTACHMENT_LOAD_OP_CLEAR,                                            // VkAttachmentLoadOp                           loadOp;
+                               VK_ATTACHMENT_STORE_OP_STORE,                                           // VkAttachmentStoreOp                          storeOp;
+                               VK_ATTACHMENT_LOAD_OP_DONT_CARE,                                        // VkAttachmentLoadOp                           stencilLoadOp;
+                               VK_ATTACHMENT_STORE_OP_DONT_CARE,                                       // VkAttachmentStoreOp                          stencilStoreOp;
+                               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,                       // VkImageLayout                                        initialLayout;
+                               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL                        // VkImageLayout                                        finalLayout;
+                       };
+
+                       for (size_t i = 0; i < m_perSampleImages.size(); ++i)
+                       {
+                               attachmentDescriptions.push_back(perSampleAttachmentDescription);
+                       }
+               }
+
+               deUint32 depthStencilAttachmentIndex = VK_ATTACHMENT_UNUSED;
+
+               if (m_useDepth || m_useStencil)
+               {
+                       depthStencilAttachmentIndex = static_cast<deUint32>(attachmentDescriptions.size());
+
+                       const VkAttachmentDescription depthStencilAttachmentDescription =
                        {
                                0u,                                                                                                                                                                     // VkAttachmentDescriptionFlags         flags;
                                m_depthStencilFormat,                                                                                                                           // VkFormat                                                     format;
@@ -1644,7 +1906,8 @@ void MultisampleRenderer::initialize (Context&                                                                    context,
                                (m_useStencil ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE),       // VkAttachmentStoreOp                          stencilStoreOp;
                                VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,                                                                       // VkImageLayout                                        initialLayout;
                                VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL                                                                        // VkImageLayout                                        finalLayout;
-                       },
+                       };
+                       attachmentDescriptions.push_back(depthStencilAttachmentDescription);
                };
 
                const VkAttachmentReference colorAttachmentReference =
@@ -1653,43 +1916,103 @@ void MultisampleRenderer::initialize (Context&                                                                 context,
                        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL                        // VkImageLayout        layout;
                };
 
+               const VkAttachmentReference inputAttachmentReference =
+               {
+                       0u,                                                                                                     // deUint32                     attachment;
+                       VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL                        // VkImageLayout        layout;
+               };
+
                const VkAttachmentReference resolveAttachmentReference =
                {
-                       1u,                                                                                                     // deUint32                     attachment;
+                       resolveAttachmentIndex,                                                         // deUint32                     attachment;
                        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL                        // VkImageLayout        layout;
                };
 
+               std::vector<VkAttachmentReference> perSampleAttachmentReferences(m_perSampleImages.size());
+               if (m_renderType == RENDER_TYPE_COPY_SAMPLES)
+               {
+                       for (size_t i = 0; i < m_perSampleImages.size(); ++i)
+                       {
+                               const VkAttachmentReference perSampleAttachmentReference =
+                               {
+                                       perSampleAttachmentIndex + static_cast<deUint32>(i),    // deUint32                     attachment;
+                                       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL                                // VkImageLayout        layout;
+                               };
+                               perSampleAttachmentReferences[i] = perSampleAttachmentReference;
+                       }
+               }
+
                const VkAttachmentReference depthStencilAttachmentReference =
                {
-                       2u,                                                                                                     // deUint32                     attachment;
+                       depthStencilAttachmentIndex,                                            // deUint32                     attachment;
                        VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL        // VkImageLayout        layout;
                };
 
-               const VkSubpassDescription subpassDescription =
-               {
-                       0u,                                                                                                                                                     // VkSubpassDescriptionFlags    flags;
-                       VK_PIPELINE_BIND_POINT_GRAPHICS,                                                                                        // VkPipelineBindPoint                  pipelineBindPoint;
-                       0u,                                                                                                                                                     // deUint32                                             inputAttachmentCount;
-                       DE_NULL,                                                                                                                                        // const VkAttachmentReference* pInputAttachments;
-                       1u,                                                                                                                                                     // deUint32                                             colorAttachmentCount;
-                       &colorAttachmentReference,                                                                                                      // const VkAttachmentReference* pColorAttachments;
-                       &resolveAttachmentReference,                                                                                            // const VkAttachmentReference* pResolveAttachments;
-                       (m_useDepth || m_useStencil ? &depthStencilAttachmentReference : DE_NULL),      // const VkAttachmentReference* pDepthStencilAttachment;
-                       0u,                                                                                                                                                     // deUint32                                             preserveAttachmentCount;
-                       DE_NULL                                                                                                                                         // const VkAttachmentReference* pPreserveAttachments;
-               };
+               std::vector<VkSubpassDescription>       subpassDescriptions;
+               std::vector<VkSubpassDependency>        subpassDependencies;
+
+               {
+                       const VkSubpassDescription renderSubpassDescription =
+                       {
+                               0u,                                                                                                                                                             // VkSubpassDescriptionFlags    flags;
+                               VK_PIPELINE_BIND_POINT_GRAPHICS,                                                                                                // VkPipelineBindPoint                  pipelineBindPoint;
+                               0u,                                                                                                                                                             // deUint32                                             inputAttachmentCount;
+                               DE_NULL,                                                                                                                                                // const VkAttachmentReference* pInputAttachments;
+                               1u,                                                                                                                                                             // deUint32                                             colorAttachmentCount;
+                               &colorAttachmentReference,                                                                                                              // const VkAttachmentReference* pColorAttachments;
+                               (m_renderType == RENDER_TYPE_RESOLVE) ? &resolveAttachmentReference : DE_NULL,  // const VkAttachmentReference* pResolveAttachments;
+                               (m_useDepth || m_useStencil ? &depthStencilAttachmentReference : DE_NULL),              // const VkAttachmentReference* pDepthStencilAttachment;
+                               0u,                                                                                                                                                             // deUint32                                             preserveAttachmentCount;
+                               DE_NULL                                                                                                                                                 // const VkAttachmentReference* pPreserveAttachments;
+                       };
+                       subpassDescriptions.push_back(renderSubpassDescription);
+               }
+
+               if (m_renderType == RENDER_TYPE_COPY_SAMPLES)
+               {
+
+                       for (size_t i = 0; i < m_perSampleImages.size(); ++i)
+                       {
+                               const VkSubpassDescription copySampleSubpassDescription =
+                               {
+                                       0u,                                                                                                     // VkSubpassDescriptionFlags            flags;
+                                       VK_PIPELINE_BIND_POINT_GRAPHICS,                                        // VkPipelineBindPoint                          pipelineBindPoint;
+                                       1u,                                                                                                     // deUint32                                                     inputAttachmentCount;
+                                       &inputAttachmentReference,                                                      // const VkAttachmentReference*         pInputAttachments;
+                                       1u,                                                                                                     // deUint32                                                     colorAttachmentCount;
+                                       &perSampleAttachmentReferences[i],                                      // const VkAttachmentReference*         pColorAttachments;
+                                       DE_NULL,                                                                                        // const VkAttachmentReference*         pResolveAttachments;
+                                       DE_NULL,                                                                                        // const VkAttachmentReference*         pDepthStencilAttachment;
+                                       0u,                                                                                                     // deUint32                                                     preserveAttachmentCount;
+                                       DE_NULL                                                                                         // const VkAttachmentReference*         pPreserveAttachments;
+                               };
+                               subpassDescriptions.push_back(copySampleSubpassDescription);
+
+                               const VkSubpassDependency copySampleSubpassDependency =
+                               {
+                                       0u,                                                                                                     // deUint32                                                     srcSubpass
+                                       1u + static_cast<deUint32>(i),                                          // deUint32                                                     dstSubpass
+                                       VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,                                      // VkPipelineStageFlags                         srcStageMask
+                                       VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,                          // VkPipelineStageFlags                         dstStageMask
+                                       VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,                           // VkAccessFlags                                        srcAccessMask
+                                       VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,                            // VkAccessFlags                                        dstAccessMask
+                                       0u,                                                                                                     // VkDependencyFlags                            dependencyFlags
+                               };
+                               subpassDependencies.push_back(copySampleSubpassDependency);
+                       }
+               }
 
                const VkRenderPassCreateInfo renderPassParams =
                {
-                       VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,                      // VkStructureType                                      sType;
-                       DE_NULL,                                                                                        // const void*                                          pNext;
-                       0u,                                                                                                     // VkRenderPassCreateFlags                      flags;
-                       numUsedAttachments,                                                                     // deUint32                                                     attachmentCount;
-                       attachmentDescriptions,                                                         // const VkAttachmentDescription*       pAttachments;
-                       1u,                                                                                                     // deUint32                                                     subpassCount;
-                       &subpassDescription,                                                            // const VkSubpassDescription*          pSubpasses;
-                       0u,                                                                                                     // deUint32                                                     dependencyCount;
-                       DE_NULL                                                                                         // const VkSubpassDependency*           pDependencies;
+                       VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,                                      // VkStructureType                                      sType;
+                       DE_NULL,                                                                                                        // const void*                                          pNext;
+                       0u,                                                                                                                     // VkRenderPassCreateFlags                      flags;
+                       (deUint32)attachmentDescriptions.size(),                                        // deUint32                                                     attachmentCount;
+                       &attachmentDescriptions[0],                                                                     // const VkAttachmentDescription*       pAttachments;
+                       (deUint32)subpassDescriptions.size(),                                           // deUint32                                                     subpassCount;
+                       &subpassDescriptions[0],                                                                        // const VkSubpassDescription*          pSubpasses;
+                       (deUint32)subpassDependencies.size(),                                           // deUint32                                                     dependencyCount;
+                       subpassDependencies.size() != 0 ? &subpassDependencies[0] : DE_NULL
                };
 
                m_renderPass = createRenderPass(vk, vkDevice, &renderPassParams);
@@ -1697,24 +2020,36 @@ void MultisampleRenderer::initialize (Context&                                                                  context,
 
        // Create framebuffer
        {
-               const VkImageView attachments[3] =
+               std::vector<VkImageView> attachments;
+               attachments.push_back(*m_colorAttachmentView);
+               if (m_renderType == RENDER_TYPE_RESOLVE)
                {
-                       *m_colorAttachmentView,
-                       *m_resolveAttachmentView,
-                       *m_depthStencilAttachmentView
-               };
+                       attachments.push_back(*m_resolveAttachmentView);
+               }
+               if (m_renderType == RENDER_TYPE_COPY_SAMPLES)
+               {
+                       for (size_t i = 0; i < m_perSampleImages.size(); ++i)
+                       {
+                               attachments.push_back(*m_perSampleImages[i]->m_attachmentView);
+                       }
+               }
+
+               if (m_useDepth || m_useStencil)
+               {
+                       attachments.push_back(*m_depthStencilAttachmentView);
+               }
 
                const VkFramebufferCreateInfo framebufferParams =
                {
-                       VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,                      // VkStructureType                              sType;
-                       DE_NULL,                                                                                        // const void*                                  pNext;
-                       0u,                                                                                                     // VkFramebufferCreateFlags             flags;
-                       *m_renderPass,                                                                          // VkRenderPass                                 renderPass;
-                       numUsedAttachments,                                                                     // deUint32                                             attachmentCount;
-                       attachments,                                                                            // const VkImageView*                   pAttachments;
-                       (deUint32)m_renderSize.x(),                                                     // deUint32                                             width;
-                       (deUint32)m_renderSize.y(),                                                     // deUint32                                             height;
-                       1u                                                                                                      // deUint32                                             layers;
+                       VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,                      // VkStructureType                                      sType;
+                       DE_NULL,                                                                                        // const void*                                          pNext;
+                       0u,                                                                                                     // VkFramebufferCreateFlags                     flags;
+                       *m_renderPass,                                                                          // VkRenderPass                                         renderPass;
+                       (deUint32)attachments.size(),                                           // deUint32                                                     attachmentCount;
+                       &attachments[0],                                                                        // const VkImageView*                           pAttachments;
+                       (deUint32)m_renderSize.x(),                                                     // deUint32                                                     width;
+                       (deUint32)m_renderSize.y(),                                                     // deUint32                                                     height;
+                       1u                                                                                                      // deUint32                                                     layers;
                };
 
                m_framebuffer = createFramebuffer(vk, vkDevice, &framebufferParams);
@@ -1734,14 +2069,64 @@ void MultisampleRenderer::initialize (Context&                                                                  context,
                };
 
                m_pipelineLayout = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
+
+               if (m_renderType == RENDER_TYPE_COPY_SAMPLES)
+               {
+
+                       // Create descriptor set layout
+                       const VkDescriptorSetLayoutBinding              layoutBinding                                   =
+                       {
+                               0u,                                                                                                                     // deUint32                                                             binding;
+                               VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,                                            // VkDescriptorType                                             descriptorType;
+                               1u,                                                                                                                     // deUint32                                                             descriptorCount;
+                               VK_SHADER_STAGE_FRAGMENT_BIT,                                                           // VkShaderStageFlags                                   stageFlags;
+                               DE_NULL,                                                                                                        // const VkSampler*                                             pImmutableSamplers;
+                       };
+
+                       const VkDescriptorSetLayoutCreateInfo   descriptorSetLayoutParams               =
+                       {
+                               VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,            // VkStructureType                                              sType
+                               DE_NULL,                                                                                                        // const void*                                                  pNext
+                               0u,                                                                                                                     // VkDescriptorSetLayoutCreateFlags             flags
+                               1u,                                                                                                                     // deUint32                                                             bindingCount
+                               &layoutBinding                                                                                          // const VkDescriptorSetLayoutBinding*  pBindings
+                       };
+                       m_copySampleDesciptorLayout     = createDescriptorSetLayout(vk, vkDevice, &descriptorSetLayoutParams);
+
+                       // Create pipeline layout
+
+                       const VkPushConstantRange                               pushConstantRange                               =
+                       {
+                               VK_SHADER_STAGE_FRAGMENT_BIT,                                                           // VkShaderStageFlags                                   stageFlags;
+                               0u,                                                                                                                     // deUint32                                                             offset;
+                               sizeof(deInt32)                                                                                         // deUint32                                                             size;
+                       };
+                       const VkPipelineLayoutCreateInfo                copySamplePipelineLayoutParams  =
+                       {
+                               VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,                          // VkStructureType                                              sType;
+                               DE_NULL,                                                                                                        // const void*                                                  pNext;
+                               0u,                                                                                                                     // VkPipelineLayoutCreateFlags                  flags;
+                               1u,                                                                                                                     // deUint32                                                             setLayoutCount;
+                               &m_copySampleDesciptorLayout.get(),                                                     // const VkDescriptorSetLayout*                 pSetLayouts;
+                               1u,                                                                                                                     // deUint32                                                             pushConstantRangeCount;
+                               &pushConstantRange                                                                                      // const VkPushConstantRange*                   pPushConstantRanges;
+                       };
+                       m_copySamplePipelineLayout              = createPipelineLayout(vk, vkDevice, &copySamplePipelineLayoutParams);
+               }
        }
 
        m_vertexShaderModule    = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("color_vert"), 0);
        m_fragmentShaderModule  = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("color_frag"), 0);
 
+       if (m_renderType == RENDER_TYPE_COPY_SAMPLES)
+       {
+               m_copySampleVertexShaderModule          = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("quad_vert"), 0);
+               m_copySampleFragmentShaderModule        = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("copy_sample_frag"), 0);
+       }
+
        // Create pipeline
        {
-               const VkPipelineShaderStageCreateInfo shaderStageParams[2] =
+               const VkPipelineShaderStageCreateInfo   shaderStageParams[2] =
                {
                        {
                                VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,            // VkStructureType                                              sType;
@@ -1763,7 +2148,7 @@ void MultisampleRenderer::initialize (Context&                                                                    context,
                        }
                };
 
-               const VkVertexInputBindingDescription vertexInputBindingDescription =
+               const VkVertexInputBindingDescription   vertexInputBindingDescription =
                {
                        0u,                                                                     // deUint32                             binding;
                        sizeof(Vertex4RGBA),                            // deUint32                             stride;
@@ -1920,6 +2305,208 @@ void MultisampleRenderer::initialize (Context&                                                                  context,
                }
        }
 
+       if (m_renderType == RENDER_TYPE_COPY_SAMPLES)
+       {
+               // Create pipelines for copying samples to single sampled images
+               {
+                       const VkPipelineShaderStageCreateInfo shaderStageParams[2] =
+                       {
+                               {
+                                       VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,            // VkStructureType                                                      sType;
+                                       DE_NULL,                                                                                                        // const void*                                                          pNext;
+                                       0u,                                                                                                                     // VkPipelineShaderStageCreateFlags                     flags;
+                                       VK_SHADER_STAGE_VERTEX_BIT,                                                                     // VkShaderStageFlagBits                                        stage;
+                                       *m_copySampleVertexShaderModule,                                                        // VkShaderModule                                                       module;
+                                       "main",                                                                                                         // const char*                                                          pName;
+                                       DE_NULL                                                                                                         // const VkSpecializationInfo*                          pSpecializationInfo;
+                               },
+                               {
+                                       VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,            // VkStructureType                                                      sType;
+                                       DE_NULL,                                                                                                        // const void*                                                          pNext;
+                                       0u,                                                                                                                     // VkPipelineShaderStageCreateFlags                     flags;
+                                       VK_SHADER_STAGE_FRAGMENT_BIT,                                                           // VkShaderStageFlagBits                                        stage;
+                                       *m_copySampleFragmentShaderModule,                                                      // VkShaderModule                                                       module;
+                                       "main",                                                                                                         // const char*                                                          pName;
+                                       DE_NULL                                                                                                         // const VkSpecializationInfo*                          pSpecializationInfo;
+                               }
+                       };
+
+                       const VkPipelineVertexInputStateCreateInfo vertexInputStateParams =
+                       {
+                               VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,              // VkStructureType                                                      sType;
+                               DE_NULL,                                                                                                                // const void*                                                          pNext;
+                               0u,                                                                                                                             // VkPipelineVertexInputStateCreateFlags        flags;
+                               0u,                                                                                                                             // deUint32                                                                     vertexBindingDescriptionCount;
+                               DE_NULL,                                                                                                                // const VkVertexInputBindingDescription*       pVertexBindingDescriptions;
+                               0u,                                                                                                                             // deUint32                                                                     vertexAttributeDescriptionCount;
+                               DE_NULL                                                                                                                 // const VkVertexInputAttributeDescription*     pVertexAttributeDescriptions;
+                       };
+
+                       // Topology is set before the pipeline creation.
+                       VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateParams =
+                       {
+                               VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,    // VkStructureType                                                      sType;
+                               DE_NULL,                                                                                                                // const void*                                                          pNext;
+                               0u,                                                                                                                             // VkPipelineInputAssemblyStateCreateFlags      flags;
+                               VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,                                                   // VkPrimitiveTopology                                          topology;
+                               false                                                                                                                   // VkBool32                                                                     primitiveRestartEnable;
+                       };
+
+                       const VkViewport viewport =
+                       {
+                               0.0f,                                           // float        x;
+                               0.0f,                                           // float        y;
+                               (float)m_renderSize.x(),        // float        width;
+                               (float)m_renderSize.y(),        // float        height;
+                               0.0f,                                           // float        minDepth;
+                               1.0f                                            // float        maxDepth;
+                       };
+
+                       const VkRect2D scissor =
+                       {
+                               { 0, 0 },                                                                                                       // VkOffset2D  offset;
+                               { (deUint32)m_renderSize.x(), (deUint32)m_renderSize.y() }      // VkExtent2D  extent;
+                       };
+
+                       const VkPipelineViewportStateCreateInfo viewportStateParams =
+                       {
+                               VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,                  // VkStructureType                                                      sType;
+                               DE_NULL,                                                                                                                // const void*                                                          pNext;
+                               0u,                                                                                                                             // VkPipelineViewportStateCreateFlags           flags;
+                               1u,                                                                                                                             // deUint32                                                                     viewportCount;
+                               &viewport,                                                                                                              // const VkViewport*                                            pViewports;
+                               1u,                                                                                                                             // deUint32                                                                     scissorCount;
+                               &scissor                                                                                                                // const VkRect2D*                                                      pScissors;
+                       };
+
+                       const VkPipelineRasterizationStateCreateInfo rasterStateParams =
+                       {
+                               VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,             // VkStructureType                                                      sType;
+                               DE_NULL,                                                                                                                // const void*                                                          pNext;
+                               0u,                                                                                                                             // VkPipelineRasterizationStateCreateFlags      flags;
+                               false,                                                                                                                  // VkBool32                                                                     depthClampEnable;
+                               false,                                                                                                                  // VkBool32                                                                     rasterizerDiscardEnable;
+                               VK_POLYGON_MODE_FILL,                                                                                   // VkPolygonMode                                                        polygonMode;
+                               VK_CULL_MODE_NONE,                                                                                              // VkCullModeFlags                                                      cullMode;
+                               VK_FRONT_FACE_COUNTER_CLOCKWISE,                                                                // VkFrontFace                                                          frontFace;
+                               VK_FALSE,                                                                                                               // VkBool32                                                                     depthBiasEnable;
+                               0.0f,                                                                                                                   // float                                                                        depthBiasConstantFactor;
+                               0.0f,                                                                                                                   // float                                                                        depthBiasClamp;
+                               0.0f,                                                                                                                   // float                                                                        depthBiasSlopeFactor;
+                               1.0f                                                                                                                    // float                                                                        lineWidth;
+                       };
+
+                       const VkPipelineColorBlendStateCreateInfo colorBlendStateParams =
+                       {
+                               VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,       // VkStructureType                                                              sType;
+                               DE_NULL,                                                                                                        // const void*                                                                  pNext;
+                               0u,                                                                                                                     // VkPipelineColorBlendStateCreateFlags                 flags;
+                               false,                                                                                                          // VkBool32                                                                             logicOpEnable;
+                               VK_LOGIC_OP_COPY,                                                                                       // VkLogicOp                                                                    logicOp;
+                               1u,                                                                                                                     // deUint32                                                                             attachmentCount;
+                               &m_colorBlendState,                                                                                     // const VkPipelineColorBlendAttachmentState*   pAttachments;
+                               { 0.0f, 0.0f, 0.0f, 0.0f }                                                                      // float                                                                                blendConstants[4];
+                       };
+
+                       const  VkPipelineMultisampleStateCreateInfo multisampleStateParams =
+                       {
+                               VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,       // VkStructureType                                                      sType
+                               DE_NULL,                                                                                                        // const void*                                                          pNext
+                               0u,                                                                                                                     // VkPipelineMultisampleStateCreateFlags        flags
+                               VK_SAMPLE_COUNT_1_BIT,                                                                          // VkSampleCountFlagBits                                        rasterizationSamples
+                               VK_FALSE,                                                                                                       // VkBool32                                                                     sampleShadingEnable
+                               0.0f,                                                                                                           // float                                                                        minSampleShading
+                               DE_NULL,                                                                                                        // const VkSampleMask*                                          pSampleMask
+                               VK_FALSE,                                                                                                       // VkBool32                                                                     alphaToCoverageEnable
+                               VK_FALSE,                                                                                                       // VkBool32                                                                     alphaToOneEnable
+                       };
+
+                       const VkGraphicsPipelineCreateInfo graphicsPipelineTemplate =
+                       {
+                               VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,        // VkStructureType                                                                      sType;
+                               DE_NULL,                                                                                        // const void*                                                                          pNext;
+                               0u,                                                                                                     // VkPipelineCreateFlags                                                        flags;
+                               2u,                                                                                                     // deUint32                                                                                     stageCount;
+                               shaderStageParams,                                                                      // const VkPipelineShaderStageCreateInfo*                       pStages;
+                               &vertexInputStateParams,                                                        // const VkPipelineVertexInputStateCreateInfo*          pVertexInputState;
+                               &inputAssemblyStateParams,                                                      // const VkPipelineInputAssemblyStateCreateInfo*        pInputAssemblyState;
+                               DE_NULL,                                                                                        // const VkPipelineTessellationStateCreateInfo*         pTessellationState;
+                               &viewportStateParams,                                                           // const VkPipelineViewportStateCreateInfo*                     pViewportState;
+                               &rasterStateParams,                                                                     // const VkPipelineRasterizationStateCreateInfo*        pRasterizationState;
+                               &multisampleStateParams,                                                        // const VkPipelineMultisampleStateCreateInfo*          pMultisampleState;
+                               DE_NULL,                                                                                        // const VkPipelineDepthStencilStateCreateInfo*         pDepthStencilState;
+                               &colorBlendStateParams,                                                         // const VkPipelineColorBlendStateCreateInfo*           pColorBlendState;
+                               (const VkPipelineDynamicStateCreateInfo*)DE_NULL,       // const VkPipelineDynamicStateCreateInfo*                      pDynamicState;
+                               *m_copySamplePipelineLayout,                                            // VkPipelineLayout                                                                     layout;
+                               *m_renderPass,                                                                          // VkRenderPass                                                                         renderPass;
+                               0u,                                                                                                     // deUint32                                                                                     subpass;
+                               0u,                                                                                                     // VkPipeline                                                                           basePipelineHandle;
+                               0u                                                                                                      // deInt32                                                                                      basePipelineIndex;
+                       };
+
+                       for (size_t i = 0; i < m_perSampleImages.size(); ++i)
+                       {
+                               VkGraphicsPipelineCreateInfo graphicsPipelineParams = graphicsPipelineTemplate;
+
+                               // Pipeline is to be used in subpasses subsequent to sample-shading subpass
+                               graphicsPipelineParams.subpass = 1u + (deUint32)i;
+
+                               m_copySamplePipelines.push_back(VkPipelineSp(new Unique<VkPipeline>(createGraphicsPipeline(vk, vkDevice, DE_NULL, &graphicsPipelineParams))));
+                       }
+               }
+
+
+               const VkDescriptorPoolSize                      descriptorPoolSize                      =
+               {
+                       VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,                                    // VkDescriptorType                                     type;
+                       1u                                                                                                              // deUint32                                                     descriptorCount;
+               };
+
+               const VkDescriptorPoolCreateInfo        descriptorPoolCreateInfo        =
+               {
+                       VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,                  // VkStructureType                                      sType
+                       DE_NULL,                                                                                                // const void*                                          pNext
+                       VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,              // VkDescriptorPoolCreateFlags          flags
+                       1u,                                                                                                     // deUint32                                                     maxSets
+                       1u,                                                                                                             // deUint32                                                     poolSizeCount
+                       &descriptorPoolSize                                                                             // const VkDescriptorPoolSize*          pPoolSizes
+               };
+
+               m_copySampleDesciptorPool = createDescriptorPool(vk, vkDevice, &descriptorPoolCreateInfo);
+
+               const VkDescriptorSetAllocateInfo       descriptorSetAllocateInfo       =
+               {
+                       VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,                 // VkStructureType                                      sType
+                       DE_NULL,                                                                                                // const void*                                          pNext
+                       *m_copySampleDesciptorPool,                                                             // VkDescriptorPool                                     descriptorPool
+                       1u,                                                                                                             // deUint32                                                     descriptorSetCount
+                       &m_copySampleDesciptorLayout.get(),                                             // const VkDescriptorSetLayout*         pSetLayouts
+               };
+
+               m_copySampleDesciptorSet = allocateDescriptorSet(vk, vkDevice, &descriptorSetAllocateInfo);
+
+               const VkDescriptorImageInfo                     imageInfo                                       =
+               {
+                       DE_NULL,
+                       *m_colorAttachmentView,
+                       VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
+               };
+               const VkWriteDescriptorSet                      descriptorWrite                         =
+               {
+                       VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,                 // VkStructureType                                      sType;
+                       DE_NULL,                                                                                // const void*                                          pNext;
+                       *m_copySampleDesciptorSet,                                              // VkDescriptorSet                                      dstSet;
+                       0u,                                                                                             // deUint32                                                     dstBinding;
+                       0u,                                                                                             // deUint32                                                     dstArrayElement;
+                       1u,                                                                                             // deUint32                                                     descriptorCount;
+                       VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,                    // VkDescriptorType                                     descriptorType;
+                       &imageInfo,                                                                             // const VkDescriptorImageInfo*         pImageInfo;
+                       DE_NULL,                                                                                // const VkDescriptorBufferInfo*        pBufferInfo;
+                       DE_NULL,                                                                                // const VkBufferView*                          pTexelBufferView;
+               };
+               vk.updateDescriptorSets(vkDevice, 1u, &descriptorWrite, 0u, DE_NULL);
+       }
+
        // Create vertex buffer
        {
                const VkBufferCreateInfo vertexBufferParams =
@@ -1974,12 +2561,23 @@ void MultisampleRenderer::initialize (Context&                                                                  context,
                depthStencilClearValue.depthStencil.depth = 1.0f;
                depthStencilClearValue.depthStencil.stencil = 0u;
 
-               const VkClearValue clearValues[3] =
+               std::vector<VkClearValue> clearValues;
+               clearValues.push_back(colorClearValue);
+               if (m_renderType == RENDER_TYPE_RESOLVE)
                {
-                       colorClearValue,
-                       colorClearValue,
-                       depthStencilClearValue
-               };
+                       clearValues.push_back(colorClearValue);
+               }
+               if (m_renderType == RENDER_TYPE_COPY_SAMPLES)
+               {
+                       for (size_t i = 0; i < m_perSampleImages.size(); ++i)
+                       {
+                               clearValues.push_back(colorClearValue);
+                       }
+               }
+               if (m_useDepth || m_useStencil)
+               {
+                       clearValues.push_back(depthStencilClearValue);
+               }
 
                const VkRenderPassBeginInfo renderPassBeginInfo =
                {
@@ -1991,13 +2589,15 @@ void MultisampleRenderer::initialize (Context&                                                                  context,
                                { 0, 0 },
                                { (deUint32)m_renderSize.x(), (deUint32)m_renderSize.y() }
                        },                                                                                                              // VkRect2D                             renderArea;
-                       numUsedAttachments,                                                                             // deUint32                             clearValueCount;
-                       clearValues                                                                                             // const VkClearValue*  pClearValues;
+                       (deUint32)clearValues.size(),                                                   // deUint32                             clearValueCount;
+                       &clearValues[0]                                                                                 // const VkClearValue*  pClearValues;
                };
 
-               const VkImageMemoryBarrier imageLayoutBarriers[] =
+               std::vector<VkImageMemoryBarrier> imageLayoutBarriers;
+
                {
-                       // color attachment image
+                       const VkImageMemoryBarrier colorImageBarrier =
+                               // color attachment image
                        {
                                VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,                 // VkStructureType                      sType;
                                DE_NULL,                                                                                // const void*                          pNext;
@@ -2009,7 +2609,12 @@ void MultisampleRenderer::initialize (Context&                                                                   context,
                                VK_QUEUE_FAMILY_IGNORED,                                                // deUint32                                     dstQueueFamilyIndex;
                                *m_colorImage,                                                                  // VkImage                                      image;
                                { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u },  // VkImageSubresourceRange      subresourceRange;
-                       },
+                       };
+                       imageLayoutBarriers.push_back(colorImageBarrier);
+               }
+               if (m_renderType == RENDER_TYPE_RESOLVE)
+               {
+                       const VkImageMemoryBarrier resolveImageBarrier =
                        // resolve attachment image
                        {
                                VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,                 // VkStructureType                      sType;
@@ -2022,7 +2627,33 @@ void MultisampleRenderer::initialize (Context&                                                                   context,
                                VK_QUEUE_FAMILY_IGNORED,                                                // deUint32                                     dstQueueFamilyIndex;
                                *m_resolveImage,                                                                // VkImage                                      image;
                                { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u },  // VkImageSubresourceRange      subresourceRange;
-                       },
+                       };
+                       imageLayoutBarriers.push_back(resolveImageBarrier);
+               }
+               if (m_renderType == RENDER_TYPE_COPY_SAMPLES)
+               {
+                       for (size_t i = 0; i < m_perSampleImages.size(); ++i)
+                       {
+                               const VkImageMemoryBarrier perSampleImageBarrier =
+                               // resolve attachment image
+                               {
+                                       VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,                 // VkStructureType                      sType;
+                                       DE_NULL,                                                                                // const void*                          pNext;
+                                       0u,                                                                                             // VkAccessFlags                        srcAccessMask;
+                                       VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,                   // VkAccessFlags                        dstAccessMask;
+                                       VK_IMAGE_LAYOUT_UNDEFINED,                                              // VkImageLayout                        oldLayout;
+                                       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,               // VkImageLayout                        newLayout;
+                                       VK_QUEUE_FAMILY_IGNORED,                                                // deUint32                                     srcQueueFamilyIndex;
+                                       VK_QUEUE_FAMILY_IGNORED,                                                // deUint32                                     dstQueueFamilyIndex;
+                                       *m_perSampleImages[i]->m_image,                                 // VkImage                                      image;
+                                       { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u },  // VkImageSubresourceRange      subresourceRange;
+                               };
+                               imageLayoutBarriers.push_back(perSampleImageBarrier);
+                       }
+               }
+               if (m_useDepth || m_useStencil)
+               {
+                       const VkImageMemoryBarrier depthStencilImageBarrier =
                        // depth/stencil attachment image
                        {
                                VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,                         // VkStructureType                      sType;
@@ -2035,7 +2666,8 @@ void MultisampleRenderer::initialize (Context&                                                                    context,
                                VK_QUEUE_FAMILY_IGNORED,                                                        // deUint32                                     dstQueueFamilyIndex;
                                *m_depthStencilImage,                                                           // VkImage                                      image;
                                { depthStencilAttachmentAspect, 0u, 1u, 0u, 1u },       // VkImageSubresourceRange      subresourceRange;
-                       },
+                       };
+                       imageLayoutBarriers.push_back(depthStencilImageBarrier);
                };
 
                m_cmdBuffer = allocateCommandBuffer(vk, vkDevice, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
@@ -2043,7 +2675,7 @@ void MultisampleRenderer::initialize (Context&                                                                    context,
                VK_CHECK(vk.beginCommandBuffer(*m_cmdBuffer, &cmdBufferBeginInfo));
 
                vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, (VkDependencyFlags)0,
-                       0u, DE_NULL, 0u, DE_NULL, numUsedAttachments, imageLayoutBarriers);
+                       0u, DE_NULL, 0u, DE_NULL, (deUint32)imageLayoutBarriers.size(), &imageLayoutBarriers[0]);
 
                vk.cmdBeginRenderPass(*m_cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
 
@@ -2058,6 +2690,19 @@ void MultisampleRenderer::initialize (Context&                                                                   context,
                        vertexBufferOffset += static_cast<VkDeviceSize>(pVertices[i].size() * sizeof(Vertex4RGBA));
                }
 
+               if (m_renderType == RENDER_TYPE_COPY_SAMPLES)
+               {
+                       // Copy each sample id to single sampled image
+                       for (deInt32 sampleId = 0; sampleId < (deInt32)m_perSampleImages.size(); ++sampleId)
+                       {
+                               vk.cmdNextSubpass(*m_cmdBuffer, VK_SUBPASS_CONTENTS_INLINE);
+                               vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, **m_copySamplePipelines[sampleId]);
+                               vk.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_copySamplePipelineLayout, 0u, 1u, &m_copySampleDesciptorSet.get(), 0u, DE_NULL);
+                               vk.cmdPushConstants(*m_cmdBuffer, *m_copySamplePipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(deInt32), &sampleId);
+                               vk.cmdDraw(*m_cmdBuffer, 4, 1, 0, 0);
+                       }
+               }
+
                vk.cmdEndRenderPass(*m_cmdBuffer);
 
                VK_CHECK(vk.endCommandBuffer(*m_cmdBuffer));
@@ -2095,7 +2740,25 @@ de::MovePtr<tcu::TextureLevel> MultisampleRenderer::render (void)
        VK_CHECK(vk.queueSubmit(queue, 1, &submitInfo, *m_fence));
        VK_CHECK(vk.waitForFences(vkDevice, 1, &m_fence.get(), true, ~(0ull) /* infinity*/));
 
-       return readColorAttachment(vk, vkDevice, queue, queueFamilyIndex, allocator, *m_resolveImage, m_colorFormat, m_renderSize.cast<deUint32>());
+       if (m_renderType == RENDER_TYPE_RESOLVE)
+       {
+               return readColorAttachment(vk, vkDevice, queue, queueFamilyIndex, allocator, *m_resolveImage, m_colorFormat, m_renderSize.cast<deUint32>());
+       }
+       else
+       {
+               return de::MovePtr<tcu::TextureLevel>();
+       }
+}
+
+de::MovePtr<tcu::TextureLevel> MultisampleRenderer::getSingleSampledImage (deUint32 sampleId)
+{
+       const DeviceInterface&          vk                                      = m_context.getDeviceInterface();
+       const VkDevice                          vkDevice                        = m_context.getDevice();
+       const VkQueue                           queue                           = m_context.getUniversalQueue();
+       const deUint32                          queueFamilyIndex        = m_context.getUniversalQueueFamilyIndex();
+       SimpleAllocator                         allocator                       (vk, vkDevice, getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice()));
+
+       return readColorAttachment(vk, vkDevice, queue, queueFamilyIndex, allocator, *m_perSampleImages[sampleId]->m_image, m_colorFormat, m_renderSize.cast<deUint32>());
 }
 
 } // anonymous