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 ©Region);
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