• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2023 The Khronos Group Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Fragment Shader Output Tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vktApiFragmentShaderOutputTests.hpp"
25 #include "vktCustomInstancesDevices.hpp"
26 #include "vkDebugReportUtil.hpp"
27 #include "tcuCommandLine.hpp"
28 #include "tcuStringTemplate.hpp"
29 #include "tcuTestLog.hpp"
30 #include "vkDeviceUtil.hpp"
31 #include "vktTestCase.hpp"
32 #include "vktTestCaseUtil.hpp"
33 #include "vktTestGroupUtil.hpp"
34 #include "vkTypeUtil.hpp"
35 #include "vkObjUtil.hpp"
36 #include "vkStrUtil.hpp"
37 #include "vkCmdUtil.hpp"
38 #include "vkImageUtil.hpp"
39 #include "vkBarrierUtil.hpp"
40 #include "vkImageWithMemory.hpp"
41 #include "vkBufferWithMemory.hpp"
42 
43 #include <map>
44 #include <tuple>
45 #include <vector>
46 #include <sstream>
47 #include <cstring>
48 #include <algorithm>
49 #include <functional>
50 #include <iostream>
51 
52 namespace
53 {
54 using namespace vk;
55 using namespace vkt;
56 
57 enum ShaderOutputCases
58 {
59     /**
60       Issue: A fragment shader can have an output Location without a corresponding pColorAttachments[Location]
61       Approach: A fragment shader writes "location = 17" and pColorAttachments contains 5 elements only    */
62     LocationNoAttachment,
63     /**
64       Issue: There can be a pColorAttachments[N] where N is not a Location
65       Approach: A fragment shader does not have an output to N location, but has other ones */
66     AttachmentNoLocation,
67     /**
68       Issue: The fragment shader output can be a different type then the attachment format (eg. UNORM vs SINT vs UINT)
69       Approach: Go through all cartesian product from R8{UNORM,SNORM,UINT,SINT} excluding the same formats and wait for validation layer answer */
70     DifferentSignedness,
71 };
72 
73 struct TestConfig
74 {
75     //                     shaderFormat -> renderFormat
76     typedef std::vector<std::pair<VkFormat, VkFormat>> Formats;
77     ShaderOutputCases theCase;
78     Formats formats;
79     std::vector<VkFormat> getShaderFormats() const;
80     std::vector<VkFormat> getRenderFormats() const;
81 
82     static const uint32_t unsignedIntColor = 123u;
83     static const int32_t signedIntColor    = 111;
84 };
85 
86 class FragmentShaderOutputInstance : public TestInstance
87 {
88 public:
89     typedef std::vector<VkFormat> Formats;
90     typedef std::vector<VkClearValue> ClearColors;
91     typedef std::vector<BufferWithMemory *> BufferPtrs;
92     typedef std::vector<de::MovePtr<ImageWithMemory>> Images;
93     typedef std::vector<de::MovePtr<BufferWithMemory>> Buffers;
94 
FragmentShaderOutputInstance(Context & context,const TestConfig & config)95     FragmentShaderOutputInstance(Context &context, const TestConfig &config) : TestInstance(context), m_config(config)
96     {
97     }
98     virtual ~FragmentShaderOutputInstance(void) = default;
99     virtual tcu::TestStatus iterate(void) override;
100 
101     Move<VkPipeline> createGraphicsPipeline(VkPipelineLayout layout, VkShaderModule vertexModule,
102                                             VkShaderModule fragmentModule, VkRenderPass renderPass,
103                                             const uint32_t subpass, const uint32_t width, const uint32_t height,
104                                             const uint32_t attachmentCount);
105     Move<VkRenderPass> createColorRenderPass(const Formats &colorFormats,
106                                              VkImageLayout finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
107     void beginColorRenderPass(VkCommandBuffer commandBuffer, VkRenderPass renderPass, VkFramebuffer framebuffer,
108                               const uint32_t width, const uint32_t height, const ClearColors &clearColors);
109     bool verifyResults(const BufferPtrs &buffers, const ClearColors &clearColors, const uint32_t width,
110                        const uint32_t height, std::string &error);
111 
112 protected:
113     const TestConfig m_config;
114 };
115 
createColorRenderPass(const Formats & formats,VkImageLayout finalLayout)116 Move<VkRenderPass> FragmentShaderOutputInstance::createColorRenderPass(const Formats &formats,
117                                                                        VkImageLayout finalLayout)
118 {
119     const uint32_t attachmentCount = static_cast<uint32_t>(formats.size());
120 
121     const VkAttachmentDescription attachmentDescriptionTemplate{
122         (VkAttachmentDescriptionFlags)0,  // VkAttachmentDescriptionFlags    flags
123         VK_FORMAT_UNDEFINED,              // VkFormat                        format
124         VK_SAMPLE_COUNT_1_BIT,            // VkSampleCountFlagBits           samples
125         VK_ATTACHMENT_LOAD_OP_CLEAR,      // VkAttachmentLoadOp              loadOp
126         VK_ATTACHMENT_STORE_OP_STORE,     // VkAttachmentStoreOp             storeOp
127         VK_ATTACHMENT_LOAD_OP_DONT_CARE,  // VkAttachmentLoadOp              stencilLoadOp
128         VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp             stencilStoreOp
129         VK_IMAGE_LAYOUT_UNDEFINED,        // VkImageLayout                   initialLayout
130         finalLayout                       // VkImageLayout                   finalLayout
131     };
132     const VkAttachmentReference attachmentReferenceTemplate{
133         0,           // uint32_t attachment;
134         finalLayout, // VkImageLayout layout;
135     };
136 
137     std::vector<VkAttachmentDescription> attachmentDescriptions(attachmentCount, attachmentDescriptionTemplate);
138     std::vector<VkAttachmentReference> attachmentReferences(attachmentCount, attachmentReferenceTemplate);
139 
140     for (uint32_t a = 0; a < attachmentCount; ++a)
141     {
142         attachmentReferences[a].attachment = a;
143         attachmentDescriptions[a].format   = formats.at(a);
144     }
145 
146     const VkSubpassDescription subpassDescription{
147         (VkSubpassDescriptionFlags)0,    // VkSubpassDescriptionFlags       flags
148         VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint             pipelineBindPoint
149         0u,                              // uint32_t                        inputAttachmentCount
150         nullptr,                         // const VkAttachmentReference*    pInputAttachments
151         attachmentCount,                 // uint32_t                        colorAttachmentCount
152         attachmentReferences.data(),     // const VkAttachmentReference*    pColorAttachments
153         nullptr,                         // const VkAttachmentReference*    pResolveAttachments
154         nullptr,                         // const VkAttachmentReference*    pDepthStencilAttachment
155         0u,                              // uint32_t                        preserveAttachmentCount
156         nullptr                          // const uint32_t*                 pPreserveAttachments
157     };
158 
159     const VkRenderPassCreateInfo renderPassInfo{
160         VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType                   sType
161         nullptr,                                   // const void*                       pNext
162         (VkRenderPassCreateFlags)0,                // VkRenderPassCreateFlags           flags
163         attachmentCount,                           // uint32_t                          attachmentCount
164         attachmentDescriptions.data(),             // const VkAttachmentDescription*    pAttachments
165         1u,                                        // uint32_t                          subpassCount
166         &subpassDescription,                       // const VkSubpassDescription*       pSubpasses
167         0u,                                        // uint32_t                          dependencyCount
168         nullptr                                    // const VkSubpassDependency*        pDependencies
169     };
170 
171     return createRenderPass(m_context.getDeviceInterface(), m_context.getDevice(), &renderPassInfo, nullptr);
172 }
173 
createGraphicsPipeline(VkPipelineLayout layout,VkShaderModule vertexModule,VkShaderModule fragmentModule,VkRenderPass renderPass,const uint32_t subpass,const uint32_t width,const uint32_t height,const uint32_t attachmentCount)174 Move<VkPipeline> FragmentShaderOutputInstance::createGraphicsPipeline(
175     VkPipelineLayout layout, VkShaderModule vertexModule, VkShaderModule fragmentModule, VkRenderPass renderPass,
176     const uint32_t subpass, const uint32_t width, const uint32_t height, const uint32_t attachmentCount)
177 {
178     const std::vector<VkViewport> viewports{makeViewport(width, height)};
179     const std::vector<VkRect2D> scissors{makeRect2D(width, height)};
180 
181     VkPipelineColorBlendAttachmentState colorBlendAttachmentStateTemplate{};
182     colorBlendAttachmentStateTemplate.colorWriteMask =
183         (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT);
184     std::vector<VkPipelineColorBlendAttachmentState> attachments(attachmentCount, colorBlendAttachmentStateTemplate);
185 
186     VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = initVulkanStructure();
187     colorBlendStateCreateInfo.attachmentCount                     = attachmentCount;
188     colorBlendStateCreateInfo.pAttachments                        = attachments.data();
189 
190     return makeGraphicsPipeline(m_context.getDeviceInterface(), m_context.getDevice(), layout, vertexModule,
191                                 VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, fragmentModule, renderPass, viewports,
192                                 scissors, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, subpass, 0u, nullptr, nullptr, nullptr,
193                                 nullptr, &colorBlendStateCreateInfo);
194 }
195 
beginColorRenderPass(VkCommandBuffer commandBuffer,VkRenderPass renderPass,VkFramebuffer framebuffer,const uint32_t width,const uint32_t height,const ClearColors & clearColors)196 void FragmentShaderOutputInstance::beginColorRenderPass(VkCommandBuffer commandBuffer, VkRenderPass renderPass,
197                                                         VkFramebuffer framebuffer, const uint32_t width,
198                                                         const uint32_t height, const ClearColors &clearColors)
199 {
200     const VkRenderPassBeginInfo renderPassBeginInfo = {
201         VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, // VkStructureType         sType;
202         nullptr,                                  // const void*             pNext;
203         renderPass,                               // VkRenderPass            renderPass;
204         framebuffer,                              // VkFramebuffer           framebuffer;
205         makeRect2D(width, height),                // VkRect2D                renderArea;
206         uint32_t(clearColors.size()),             // uint32_t                clearValueCount;
207         clearColors.data()                        // const VkClearValue*     pClearValues;
208     };
209 
210     m_context.getDeviceInterface().cmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
211 }
212 
213 class FragmentShaderOutputCase : public TestCase
214 {
215 public:
FragmentShaderOutputCase(tcu::TestContext & testCtx,const TestConfig & conf,const std::string & name)216     FragmentShaderOutputCase(tcu::TestContext &testCtx, const TestConfig &conf, const std::string &name)
217         : TestCase(testCtx, name)
218         , m_config(conf)
219     {
220     }
221     virtual TestInstance *createInstance(Context &context) const override;
222     virtual void checkSupport(Context &context) const override;
223     virtual void initPrograms(SourceCollections &programs) const override;
224 
225 private:
226     const TestConfig m_config;
227 };
228 
getShaderFormats() const229 std::vector<VkFormat> TestConfig::getShaderFormats() const
230 {
231     std::vector<VkFormat> result(formats.size());
232     std::transform(formats.begin(), formats.end(), result.begin(),
233                    [](const TestConfig::Formats::value_type &pair) { return pair.first; });
234     return result;
235 }
236 
getRenderFormats() const237 std::vector<VkFormat> TestConfig::getRenderFormats() const
238 {
239     std::vector<VkFormat> result(formats.size());
240     std::transform(formats.begin(), formats.end(), result.begin(),
241                    [](const TestConfig::Formats::value_type &pair) { return pair.second; });
242     return result;
243 }
244 
checkSupport(Context & context) const245 void FragmentShaderOutputCase::checkSupport(Context &context) const
246 {
247     const std::vector<VkFormat> renderFormats = m_config.getRenderFormats();
248 
249     VkPhysicalDeviceProperties deviceProps{};
250     context.getInstanceInterface().getPhysicalDeviceProperties(context.getPhysicalDevice(), &deviceProps);
251     if (m_config.theCase == LocationNoAttachment)
252     {
253         if (uint32_t(renderFormats.size() + 1u) > deviceProps.limits.maxColorAttachments)
254         {
255             std::stringstream s;
256             s << "Shader output location (" << (renderFormats.size() + 1u) << ") exceeds "
257               << "VkPhysicalDeviceLimits::maxColorAttachments (" << deviceProps.limits.maxColorAttachments << ')';
258             s.flush();
259             TCU_THROW(NotSupportedError, s.str());
260         }
261     }
262     else
263     {
264         if (uint32_t(renderFormats.size()) > deviceProps.limits.maxColorAttachments)
265         {
266             std::stringstream s;
267             s << "Used attachment count (" << renderFormats.size() << ") exceeds "
268               << "VkPhysicalDeviceLimits::maxColorAttachments (" << deviceProps.limits.maxColorAttachments << ')';
269             s.flush();
270             TCU_THROW(NotSupportedError, s.str());
271         }
272     }
273 
274     VkFormatProperties formatProps{};
275     for (VkFormat format : renderFormats)
276     {
277         context.getInstanceInterface().getPhysicalDeviceFormatProperties(context.getPhysicalDevice(), format,
278                                                                          &formatProps);
279         if ((formatProps.optimalTilingFeatures &
280              (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT)) !=
281             (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT))
282         {
283             TCU_THROW(NotSupportedError,
284                       "Unable to find a format with "
285                       "VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT & VK_FORMAT_FEATURE_TRANSFER_SRC_BIT supported");
286         }
287         formatProps = {};
288     }
289 }
290 
initPrograms(SourceCollections & programs) const291 void FragmentShaderOutputCase::initPrograms(SourceCollections &programs) const
292 {
293     const uint32_t attachments = static_cast<uint32_t>(m_config.formats.size());
294     const auto formats         = m_config.getShaderFormats();
295     auto type                  = [](VkFormat format)
296     {
297         if (isUintFormat(format))
298             return "uvec4";
299         else if (isIntFormat(format))
300             return "ivec4";
301         else
302             return "vec4";
303     };
304     auto value = [](VkFormat format)
305     {
306         std::ostringstream os;
307         if (isUintFormat(format))
308         {
309             os << '(' << TestConfig::unsignedIntColor << ',' << TestConfig::unsignedIntColor << ','
310                << TestConfig::unsignedIntColor << ',' << TestConfig::unsignedIntColor << ')';
311         }
312         else if (isIntFormat(format))
313         {
314             os << '(' << TestConfig::signedIntColor << ',' << TestConfig::signedIntColor << ','
315                << TestConfig::signedIntColor << ',' << TestConfig::signedIntColor << ')';
316         }
317         else
318         {
319             os << "(1.0,1.0,1.0,1.0)";
320         }
321         return os.str();
322     };
323 
324     const uint32_t magicLoc = attachments / 2;
325 
326     std::ostringstream frag;
327     frag << "#version 450" << std::endl;
328     for (uint32_t loc = 0; loc < attachments; ++loc)
329     {
330         if (magicLoc == loc)
331         {
332             if (m_config.theCase == LocationNoAttachment)
333             {
334                 frag << "layout(location = " << attachments << ") out " << type(formats.at(loc)) << " color" << loc
335                      << ';' << std::endl;
336                 continue;
337             }
338             else if (m_config.theCase == AttachmentNoLocation)
339             {
340                 continue;
341             }
342         }
343         frag << "layout(location = " << loc << ") out " << type(formats.at(loc)) << " color" << loc << ';' << std::endl;
344     }
345     frag << "void main() {" << std::endl;
346     for (uint32_t loc = 0; loc < attachments; ++loc)
347     {
348         if (magicLoc == loc && m_config.theCase == AttachmentNoLocation)
349         {
350             continue;
351         }
352         frag << "  color" << loc << " = " << type(formats.at(loc)) << value(formats.at(loc)) << ';' << std::endl;
353     }
354     frag << '}' << std::endl;
355     frag.flush();
356 
357     // traditional pass-through vertex shader
358     const std::string vert(R"glsl(
359     #version 450
360     layout(location=0) in vec4 pos;
361     void main() {
362         gl_Position = vec4(pos.xyz, 1.0);
363     }
364     )glsl");
365 
366     programs.glslSources.add("frag") << glu::FragmentSource(frag.str());
367     programs.glslSources.add("vert") << glu::VertexSource(vert);
368 }
369 
createInstance(Context & context) const370 TestInstance *FragmentShaderOutputCase::createInstance(Context &context) const
371 {
372     return (new FragmentShaderOutputInstance(context, m_config));
373 }
374 
375 namespace ut
376 {
377 
createImage(const DeviceInterface & vkd,VkDevice device,Allocator & allocator,VkFormat format,uint32_t width,uint32_t height)378 de::MovePtr<ImageWithMemory> createImage(const DeviceInterface &vkd, VkDevice device, Allocator &allocator,
379                                          VkFormat format, uint32_t width, uint32_t height)
380 {
381     const VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
382     const VkImageCreateInfo imageCreateInfo{
383         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
384         nullptr,                             // const void* pNext;
385         VkImageCreateFlags(0),               // VkImageCreateFlags flags;
386         VK_IMAGE_TYPE_2D,                    // VkImageType imageType;
387         format,                              // VkFormat format;
388         {width, height, 1u},                 // VkExtent3D extent;
389         1u,                                  // uint32_t mipLevels;
390         1u,                                  // uint32_t arrayLayers;
391         VK_SAMPLE_COUNT_1_BIT,               // VkSampleCountFlagBits samples;
392         VK_IMAGE_TILING_OPTIMAL,             // VkImageTiling tiling;
393         imageUsage,                          // VkImageUsageFlags usage;
394         VK_SHARING_MODE_EXCLUSIVE,           // VkSharingMode sharingMode;
395         0u,                                  // uint32_t queueFamilyIndexCount;
396         nullptr,                             // const uint32_t* pQueueFamilyIndices;
397         VK_IMAGE_LAYOUT_UNDEFINED            // VkImageLayout initialLayout;
398     };
399 
400     return de::MovePtr<ImageWithMemory>(
401         new ImageWithMemory(vkd, device, allocator, imageCreateInfo, MemoryRequirement::Any));
402 }
403 
createImageView(const DeviceInterface & vkd,VkDevice device,VkFormat format,VkImage image)404 Move<VkImageView> createImageView(const DeviceInterface &vkd, VkDevice device, VkFormat format, VkImage image)
405 {
406     const VkImageSubresourceRange subresourceRange =
407         makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
408     const VkImageViewCreateInfo viewCreateInfo{
409         VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
410         nullptr,                                  // const void* pNext;
411         VkImageViewCreateFlags(0),                // VkImageViewCreateFlags flags;
412         image,                                    // VkImage image;
413         VK_IMAGE_VIEW_TYPE_2D,                    // VkImageViewType viewType;
414         format,                                   // VkFormat format;
415         makeComponentMappingRGBA(),               // VkComponentMapping  components;
416         subresourceRange                          // VkImageSubresourceRange subresourceRange;
417     };
418 
419     return ::vk::createImageView(vkd, device, &viewCreateInfo, nullptr);
420 }
421 
createBuffer(const DeviceInterface & vkd,VkDevice device,Allocator & allocator,VkFormat format,uint32_t width,uint32_t height)422 de::MovePtr<BufferWithMemory> createBuffer(const DeviceInterface &vkd, VkDevice device, Allocator &allocator,
423                                            VkFormat format, uint32_t width, uint32_t height)
424 {
425     const VkBufferCreateInfo info =
426         makeBufferCreateInfo(tcu::getPixelSize(mapVkFormat(format)) * width * height, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
427     return de::MovePtr<BufferWithMemory>(new BufferWithMemory(
428         vkd, device, allocator, info, (MemoryRequirement::HostVisible | MemoryRequirement::Coherent)));
429 }
430 
makeClearColors(const std::vector<VkFormat> & formats)431 FragmentShaderOutputInstance::ClearColors makeClearColors(const std::vector<VkFormat> &formats)
432 {
433     FragmentShaderOutputInstance::ClearColors clearColors;
434     float floatStep       = 0.5f;
435     uint32_t unsignedStep = 128u;
436     int32_t signedStep    = 64;
437 
438     for (VkFormat format : formats)
439     {
440         if (isUnormFormat(format))
441         {
442             clearColors.push_back(
443                 makeClearValueColorF32((floatStep / 2.0f), (floatStep / 4.0f), (floatStep / 8.0f), 1.0f));
444             floatStep /= 2.0f;
445         }
446         else if (isUintFormat(format))
447         {
448             clearColors.push_back(
449                 makeClearValueColorU32((unsignedStep / 2), (unsignedStep / 4), (unsignedStep / 8), 255u));
450             unsignedStep /= 2;
451         }
452         else
453         {
454             clearColors.push_back(makeClearValueColorI32((signedStep / 2), (signedStep / 4), (signedStep / 8), 127));
455             signedStep /= 2;
456         }
457     }
458     return clearColors;
459 }
460 
461 } // namespace ut
462 
463 // Enable definition if you want to see attachments content
464 // #define PRINT_BUFFER
verifyResults(const BufferPtrs & buffers,const ClearColors & clearColors,const uint32_t width,const uint32_t height,std::string & error)465 bool FragmentShaderOutputInstance::verifyResults(const BufferPtrs &buffers, const ClearColors &clearColors,
466                                                  const uint32_t width, const uint32_t height, std::string &error)
467 {
468     bool result                               = false;
469     const std::vector<VkFormat> shaderFormats = m_config.getShaderFormats();
470     const std::vector<VkFormat> renderFormats = m_config.getRenderFormats();
471     const uint32_t attachments                = static_cast<uint32_t>(buffers.size());
472 
473     auto toleq = [](float a, float b, float tol) { return ((b >= (a - tol)) && (b <= (a + tol))); };
474 
475     auto isBufferUnchanged = [&](const uint32_t bufferIndex) -> bool
476     {
477         const VkFormat renderFormat = renderFormats.at(bufferIndex);
478         tcu::ConstPixelBufferAccess pixels(mapVkFormat(renderFormat), int32_t(width), int32_t(height), 1,
479                                            buffers.at(bufferIndex)->getAllocation().getHostPtr());
480         for (int y = 0; y < int32_t(height); ++y)
481             for (int x = 0; x < int32_t(width); ++x)
482             {
483                 if (isUintFormat(renderFormat))
484                 {
485                     if (pixels.getPixelUint(x, y).x() != clearColors.at(bufferIndex).color.uint32[0])
486                         return false;
487                 }
488                 else if (isIntFormat(renderFormat))
489                 {
490                     if (pixels.getPixelInt(x, y).x() != clearColors.at(bufferIndex).color.int32[0])
491                         return false;
492                 }
493                 else
494                 {
495                     DE_ASSERT(isUnormFormat(renderFormat) || isSnormFormat(renderFormat));
496                     if (!toleq(pixels.getPixel(x, y).x(), clearColors.at(bufferIndex).color.float32[0], 0.001f))
497                         return false;
498                 }
499             }
500         return true;
501     };
502     auto isBufferRendered = [&](const uint32_t bufferIndex) -> bool
503     {
504         const VkFormat shaderFormat = shaderFormats.at(bufferIndex);
505         const VkFormat renderFormat = renderFormats.at(bufferIndex);
506         tcu::ConstPixelBufferAccess pixels(mapVkFormat(renderFormat), int32_t(width), int32_t(height), 1,
507                                            buffers.at(bufferIndex)->getAllocation().getHostPtr());
508         for (int32_t y = 0; y < int32_t(height); ++y)
509             for (int32_t x = 0; x < int32_t(width); ++x)
510             {
511                 if (isUintFormat(renderFormat))
512                 {
513                     const uint32_t expected =
514                         isIntFormat(shaderFormat) ? uint32_t(TestConfig::signedIntColor) : TestConfig::unsignedIntColor;
515                     if (pixels.getPixelUint(x, y).x() != expected)
516                         return false;
517                 }
518                 else if (isIntFormat(renderFormat))
519                 {
520                     const int32_t expected =
521                         isIntFormat(shaderFormat) ? TestConfig::signedIntColor : int32_t(TestConfig::unsignedIntColor);
522                     if (pixels.getPixelInt(x, y).x() != expected)
523                         return false;
524                 }
525                 else
526                 {
527                     DE_ASSERT(isUnormFormat(renderFormat) || isSnormFormat(renderFormat));
528                     if (pixels.getPixel(x, y).x() != 1.0f)
529                         return false;
530                 }
531             }
532         return true;
533     };
534 
535 #ifdef PRINT_BUFFER
536     auto printBuffer = [&](const uint32_t bufferIndex) -> void
537     {
538         const VkFormat format = renderFormats.at(bufferIndex);
539         tcu::ConstPixelBufferAccess pixels(mapVkFormat(format), int32_t(width), int32_t(height), 1,
540                                            buffers.at(bufferIndex)->getAllocation().getHostPtr());
541         std::cout << "Attachment[" << bufferIndex << "] { ";
542         for (int x = 0; x < 4 && x < int32_t(width); ++x)
543         {
544             if (x)
545                 std::cout << "; ";
546             if (isUintFormat(format))
547             {
548                 std::cout << pixels.getPixelUint(x, 0)[x] << " | " << clearColors.at(bufferIndex).color.uint32[x];
549             }
550             else if (isIntFormat(format))
551             {
552                 std::cout << pixels.getPixelInt(x, 0)[x] << " | " << clearColors.at(bufferIndex).color.int32[x];
553             }
554             else
555             {
556                 DE_ASSERT(isUnormFormat(format) || isSnormFormat(format));
557                 std::cout << pixels.getPixel(x, 0)[x] << " | " << clearColors.at(bufferIndex).color.float32[x];
558             }
559         }
560         std::cout << " }" << std::endl;
561     };
562     for (uint32_t i = 0; i < attachments; ++i)
563         printBuffer(i);
564 #endif
565 
566     if (m_config.theCase == LocationNoAttachment || m_config.theCase == AttachmentNoLocation)
567     {
568         const uint32_t expectedLocation = attachments / 2;
569         result                          = isBufferUnchanged(expectedLocation);
570         for (uint32_t a = 0; result && a < attachments; ++a)
571         {
572             if (expectedLocation == a)
573                 continue;
574             result = isBufferRendered(a);
575         }
576     }
577     else
578     {
579         DE_ASSERT(m_config.theCase == DifferentSignedness);
580         result = true;
581         for (uint32_t a = 0; result && a < attachments; ++a)
582             result = isBufferRendered(a);
583     }
584 
585     if (!result)
586         error = "One or more attachments rendered incorrectly";
587 
588     return result;
589 }
590 
iterate(void)591 tcu::TestStatus FragmentShaderOutputInstance::iterate(void)
592 {
593     const DeviceInterface &vkd = m_context.getDeviceInterface();
594     const VkDevice device      = m_context.getDevice();
595     const VkQueue queue        = m_context.getUniversalQueue();
596     const uint32_t familyIndex = m_context.getUniversalQueueFamilyIndex();
597     Allocator &allocator       = m_context.getDefaultAllocator();
598 
599     const uint32_t width  = 64u;
600     const uint32_t height = 64u;
601 
602     Move<VkShaderModule> vertex   = createShaderModule(vkd, device, m_context.getBinaryCollection().get("vert"));
603     Move<VkShaderModule> fragment = createShaderModule(vkd, device, m_context.getBinaryCollection().get("frag"));
604 
605     const std::vector<float> vertices{+1.f, -1.f, 0.f, 0.f, -1.f, -1.f, 0.f, 0.f, -1.f, +1.f, 0.f, 0.f,
606                                       -1.f, +1.f, 0.f, 0.f, +1.f, +1.f, 0.f, 0.f, +1.f, -1.f, 0.f, 0.f};
607     const VkBufferCreateInfo vertexInfo =
608         makeBufferCreateInfo(sizeof(float) * vertices.size(), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
609     BufferWithMemory vertexBuff(vkd, device, allocator, vertexInfo,
610                                 (MemoryRequirement::HostVisible | MemoryRequirement::Coherent));
611     deMemcpy(vertexBuff.getAllocation().getHostPtr(), vertices.data(), static_cast<size_t>(vertexInfo.size));
612 
613     const uint32_t attachments    = static_cast<uint32_t>(m_config.formats.size());
614     const Formats formats         = m_config.getRenderFormats();
615     const ClearColors clearColors = ut::makeClearColors(formats);
616 
617     Images images(attachments);
618     Buffers buffers(attachments);
619     BufferPtrs bufferPtrs(attachments);
620     std::vector<Move<VkImageView>> imageViews(attachments);
621     std::vector<VkImageView> views(attachments);
622     for (uint32_t i = 0; i < attachments; ++i)
623     {
624         images[i]     = ut::createImage(vkd, device, allocator, m_config.formats.at(i).second, width, height);
625         buffers[i]    = ut::createBuffer(vkd, device, allocator, m_config.formats.at(i).second, width, height);
626         imageViews[i] = ut::createImageView(vkd, device, m_config.formats.at(i).second, **images[i]);
627         views[i]      = *imageViews[i];
628         bufferPtrs[i] = buffers[i].get();
629     }
630 
631     const VkImageSubresourceLayers sresLayers = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1u);
632     const VkBufferImageCopy copyRegion        = makeBufferImageCopy(makeExtent3D(width, height, 1u), sresLayers);
633     const VkMemoryBarrier preCopy =
634         makeMemoryBarrier(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
635 
636     Move<VkRenderPass> renderPass = createColorRenderPass(formats);
637     Move<VkFramebuffer> framebuffer =
638         makeFramebuffer(vkd, device, *renderPass, attachments, views.data(), width, height);
639     Move<VkPipelineLayout> layout = makePipelineLayout(vkd, device);
640     Move<VkPipeline> pipeline =
641         createGraphicsPipeline(*layout, *vertex, *fragment, *renderPass, 0u, width, height, attachments);
642 
643     Move<VkCommandPool> cmdPool     = makeCommandPool(vkd, device, familyIndex);
644     Move<VkCommandBuffer> cmdbuffer = allocateCommandBuffer(vkd, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
645 
646     beginCommandBuffer(vkd, *cmdbuffer);
647     vkd.cmdBindPipeline(*cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
648     vkd.cmdBindVertexBuffers(*cmdbuffer, 0u, 1u, &vertexBuff.get(), &static_cast<const VkDeviceSize &>(0));
649     beginColorRenderPass(*cmdbuffer, *renderPass, *framebuffer, width, height, clearColors);
650     vkd.cmdDraw(*cmdbuffer, uint32_t(vertices.size() / 4), 1u, 0u, 0u);
651     endRenderPass(vkd, *cmdbuffer);
652     vkd.cmdPipelineBarrier(*cmdbuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
653                            VK_DEPENDENCY_BY_REGION_BIT, 1u, &preCopy, 0u, nullptr, 0u, nullptr);
654     for (uint32_t i = 0; i < attachments; ++i)
655     {
656         vkd.cmdCopyImageToBuffer(*cmdbuffer, **images[i], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, **buffers[i], 1u,
657                                  &copyRegion);
658     }
659     endCommandBuffer(vkd, *cmdbuffer);
660     submitCommandsAndWait(vkd, device, queue, *cmdbuffer);
661 
662     std::string error;
663     const bool verdict = verifyResults(bufferPtrs, clearColors, width, height, error);
664 
665     return verdict ? tcu::TestStatus::pass(std::string()) : tcu::TestStatus::fail(error);
666 }
667 
668 typedef std::tuple<VkFormat, std::string, bool> FormatWithName;
makeTitle(const TestConfig::Formats & formats,const std::vector<FormatWithName> & names)669 std::string makeTitle(const TestConfig::Formats &formats, const std::vector<FormatWithName> &names)
670 {
671     auto findName = [&](VkFormat format)
672     {
673         return std::find_if(names.begin(), names.end(),
674                             [&](const FormatWithName &name) { return std::get<VkFormat>(name) == format; });
675     };
676     std::ostringstream os;
677     for (const auto &pair : formats)
678     {
679         auto shaderName = findName(pair.first);
680         DE_ASSERT(shaderName != names.end());
681         auto renderName = findName(pair.second);
682         DE_ASSERT(renderName != names.end());
683         if (os.tellp() > 0)
684             os << '_';
685         os << std::get<std::string>(*shaderName) << 2 << std::get<std::string>(*renderName);
686     }
687     os.flush();
688     return os.str();
689 }
690 
691 } // unnamed namespace
692 
693 namespace vkt
694 {
695 namespace api
696 {
697 
createFragmentShaderOutputTests(tcu::TestContext & testCtx)698 tcu::TestCaseGroup *createFragmentShaderOutputTests(tcu::TestContext &testCtx)
699 {
700     const std::vector<FormatWithName> formatsWithNames{
701         {VK_FORMAT_R8_UNORM, "unorm", false},
702         {VK_FORMAT_R8_SNORM, "snorm", false},
703         {VK_FORMAT_R8_UINT, "uint", true},
704         {VK_FORMAT_R8_SINT, "sint", true},
705     };
706     struct
707     {
708         ShaderOutputCases aCase;
709         const std::string name;
710         const bool signedness;
711     } const cases[]{
712         {LocationNoAttachment, "location_no_attachment", false},
713         {AttachmentNoLocation, "attachment_no_location", false},
714         {DifferentSignedness, "different_signedness", true},
715     };
716 
717     TestConfig::Formats signednessFormats;
718     signednessFormats.reserve(formatsWithNames.size() * formatsWithNames.size());
719     for (const FormatWithName &shaderFormat : formatsWithNames)
720         for (const FormatWithName &renderFormat : formatsWithNames)
721         {
722             if (!(std::get<bool>(shaderFormat) ^ std::get<bool>(renderFormat)))
723             {
724                 signednessFormats.emplace_back(std::get<VkFormat>(shaderFormat), std::get<VkFormat>(renderFormat));
725             }
726         }
727 
728     de::MovePtr<tcu::TestCaseGroup> root(new tcu::TestCaseGroup(
729         testCtx, "fragment_shader_output", "Verify fragment shader output with multiple attachments"));
730     for (const auto &aCase : cases)
731     {
732         de::MovePtr<tcu::TestCaseGroup> formatGroup(new tcu::TestCaseGroup(testCtx, aCase.name.c_str(), ""));
733         if (aCase.signedness)
734         {
735             for (TestConfig::Formats::size_type i = 0; i < signednessFormats.size(); ++i)
736                 for (TestConfig::Formats::size_type j = 0; j < signednessFormats.size(); ++j)
737                 {
738                     if ((signednessFormats[i].first == signednessFormats[j].first) ||
739                         (signednessFormats[i].second == signednessFormats[j].second))
740                         continue;
741 
742                     TestConfig config;
743                     config.theCase = aCase.aCase;
744                     config.formats.emplace_back(signednessFormats[i]);
745                     config.formats.emplace_back(signednessFormats[j]);
746 
747                     const std::string title = makeTitle(config.formats, formatsWithNames);
748                     formatGroup->addChild(new FragmentShaderOutputCase(testCtx, config, title));
749                 }
750         }
751         else
752         {
753             std::vector<FormatWithName> fwns(formatsWithNames);
754 
755             while (std::next_permutation(fwns.begin(), fwns.end()))
756             {
757                 TestConfig config;
758                 config.theCase = aCase.aCase;
759 
760                 for (const FormatWithName &name : fwns)
761                 {
762                     config.formats.emplace_back(std::get<VkFormat>(name), std::get<VkFormat>(name));
763                 }
764                 const std::string title = makeTitle(config.formats, formatsWithNames);
765                 formatGroup->addChild(new FragmentShaderOutputCase(testCtx, config, title));
766             }
767         }
768         root->addChild(formatGroup.release());
769     }
770 
771     return root.release();
772 }
773 
774 } // namespace api
775 } // namespace vkt
776