1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2016 The Khronos Group Inc.
6  * Copyright (c) 2014 The Android Open Source Project
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21  * \file
22  * \brief Scissor multi viewport tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktFragmentOperationsScissorMultiViewportTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 
28 #include "vkDefs.hpp"
29 #include "vkRefUtil.hpp"
30 #include "vkTypeUtil.hpp"
31 #include "vkMemUtil.hpp"
32 #include "vkPrograms.hpp"
33 #include "vkImageUtil.hpp"
34 #include "vkQueryUtil.hpp"
35 #include "vkCmdUtil.hpp"
36 #include "vkObjUtil.hpp"
37 
38 #include "tcuTestLog.hpp"
39 #include "tcuVector.hpp"
40 #include "tcuImageCompare.hpp"
41 #include "tcuTextureUtil.hpp"
42 
43 #include "deUniquePtr.hpp"
44 #include "deMath.h"
45 
46 namespace vkt
47 {
48 namespace FragmentOperations
49 {
50 using namespace vk;
51 using de::MovePtr;
52 using de::UniquePtr;
53 using tcu::IVec2;
54 using tcu::IVec4;
55 using tcu::Vec2;
56 using tcu::Vec4;
57 
58 namespace
59 {
60 
61 enum Constants
62 {
63     MIN_MAX_VIEWPORTS = 16, //!< Minimum number of viewports for an implementation supporting multiViewport.
64 };
65 
66 template <typename T>
sizeInBytes(const std::vector<T> & vec)67 inline VkDeviceSize sizeInBytes(const std::vector<T> &vec)
68 {
69     return vec.size() * sizeof(vec[0]);
70 }
71 
makeImageCreateInfo(const VkFormat format,const IVec2 & size,VkImageUsageFlags usage)72 VkImageCreateInfo makeImageCreateInfo(const VkFormat format, const IVec2 &size, VkImageUsageFlags usage)
73 {
74     const VkImageCreateInfo imageParams = {
75         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
76         DE_NULL,                             // const void* pNext;
77         (VkImageCreateFlags)0,               // VkImageCreateFlags flags;
78         VK_IMAGE_TYPE_2D,                    // VkImageType imageType;
79         format,                              // VkFormat format;
80         makeExtent3D(size.x(), size.y(), 1), // VkExtent3D extent;
81         1u,                                  // uint32_t mipLevels;
82         1u,                                  // uint32_t arrayLayers;
83         VK_SAMPLE_COUNT_1_BIT,               // VkSampleCountFlagBits samples;
84         VK_IMAGE_TILING_OPTIMAL,             // VkImageTiling tiling;
85         usage,                               // VkImageUsageFlags usage;
86         VK_SHARING_MODE_EXCLUSIVE,           // VkSharingMode sharingMode;
87         0u,                                  // uint32_t queueFamilyIndexCount;
88         DE_NULL,                             // const uint32_t* pQueueFamilyIndices;
89         VK_IMAGE_LAYOUT_UNDEFINED,           // VkImageLayout initialLayout;
90     };
91     return imageParams;
92 }
93 
makeGraphicsPipeline(const DeviceInterface & vk,const VkDevice device,const VkPipelineLayout pipelineLayout,const VkRenderPass renderPass,const VkShaderModule vertexModule,const VkShaderModule geometryModule,const VkShaderModule fragmentModule,const IVec2 renderSize,const int numViewports,const std::vector<IVec4> scissors)94 Move<VkPipeline> makeGraphicsPipeline(const DeviceInterface &vk, const VkDevice device,
95                                       const VkPipelineLayout pipelineLayout, const VkRenderPass renderPass,
96                                       const VkShaderModule vertexModule, const VkShaderModule geometryModule,
97                                       const VkShaderModule fragmentModule, const IVec2 renderSize,
98                                       const int numViewports, const std::vector<IVec4> scissors)
99 {
100     const VkViewport defaultViewport = makeViewport(renderSize);
101     const std::vector<VkViewport> viewports(numViewports, defaultViewport);
102 
103     DE_ASSERT(numViewports == static_cast<int>(scissors.size()));
104 
105     std::vector<VkRect2D> rectScissors;
106     rectScissors.reserve(numViewports);
107 
108     for (std::vector<IVec4>::const_iterator it = scissors.begin(); it != scissors.end(); ++it)
109     {
110         const VkRect2D rect = {
111             makeOffset2D(it->x(), it->y()),
112             makeExtent2D(static_cast<uint32_t>(it->z()), static_cast<uint32_t>(it->w())),
113         };
114         rectScissors.push_back(rect);
115     }
116 
117     return vk::makeGraphicsPipeline(vk,             // const DeviceInterface&            vk
118                                     device,         // const VkDevice                    device
119                                     pipelineLayout, // const VkPipelineLayout            pipelineLayout
120                                     vertexModule,   // const VkShaderModule              vertexShaderModule
121                                     DE_NULL,        // const VkShaderModule              tessellationControlModule
122                                     DE_NULL,        // const VkShaderModule              tessellationEvalModule
123                                     geometryModule, // const VkShaderModule              geometryShaderModule
124                                     fragmentModule, // const VkShaderModule              fragmentShaderModule
125                                     renderPass,     // const VkRenderPass                renderPass
126                                     viewports,      // const std::vector<VkViewport>&    viewports
127                                     rectScissors,   // const std::vector<VkRect2D>&      scissors
128                                     VK_PRIMITIVE_TOPOLOGY_POINT_LIST); // const VkPrimitiveTopology         topology
129 }
130 
generateScissors(const int numScissors,const IVec2 & renderSize)131 std::vector<IVec4> generateScissors(const int numScissors, const IVec2 &renderSize)
132 {
133     // Scissor rects will be arranged in a grid-like fashion.
134 
135     const int numCols    = deCeilFloatToInt32(deFloatSqrt(static_cast<float>(numScissors)));
136     const int numRows    = deCeilFloatToInt32(static_cast<float>(numScissors) / static_cast<float>(numCols));
137     const int rectWidth  = renderSize.x() / numCols;
138     const int rectHeight = renderSize.y() / numRows;
139 
140     std::vector<IVec4> scissors;
141     scissors.reserve(numScissors);
142 
143     int x = 0;
144     int y = 0;
145 
146     for (int scissorNdx = 0; scissorNdx < numScissors; ++scissorNdx)
147     {
148         const bool nextRow = (scissorNdx != 0) && (scissorNdx % numCols == 0);
149         if (nextRow)
150         {
151             x = 0;
152             y += rectHeight;
153         }
154 
155         scissors.push_back(IVec4(x, y, rectWidth, rectHeight));
156 
157         x += rectWidth;
158     }
159 
160     return scissors;
161 }
162 
generateColors(const int numColors)163 std::vector<Vec4> generateColors(const int numColors)
164 {
165     const Vec4 colors[] = {
166         Vec4(0.18f, 0.42f, 0.17f, 1.0f), Vec4(0.29f, 0.62f, 0.28f, 1.0f), Vec4(0.59f, 0.84f, 0.44f, 1.0f),
167         Vec4(0.96f, 0.95f, 0.72f, 1.0f), Vec4(0.94f, 0.55f, 0.39f, 1.0f), Vec4(0.82f, 0.19f, 0.12f, 1.0f),
168         Vec4(0.46f, 0.15f, 0.26f, 1.0f), Vec4(0.24f, 0.14f, 0.24f, 1.0f), Vec4(0.49f, 0.31f, 0.26f, 1.0f),
169         Vec4(0.78f, 0.52f, 0.33f, 1.0f), Vec4(0.94f, 0.82f, 0.31f, 1.0f), Vec4(0.98f, 0.65f, 0.30f, 1.0f),
170         Vec4(0.22f, 0.65f, 0.53f, 1.0f), Vec4(0.67f, 0.81f, 0.91f, 1.0f), Vec4(0.43f, 0.44f, 0.75f, 1.0f),
171         Vec4(0.26f, 0.24f, 0.48f, 1.0f),
172     };
173 
174     DE_ASSERT(numColors <= DE_LENGTH_OF_ARRAY(colors));
175 
176     return std::vector<Vec4>(colors, colors + numColors);
177 }
178 
179 //! Renders a colorful grid of rectangles.
generateReferenceImage(const tcu::TextureFormat format,const IVec2 & renderSize,const Vec4 & clearColor,const std::vector<IVec4> & scissors,const std::vector<Vec4> & scissorColors)180 tcu::TextureLevel generateReferenceImage(const tcu::TextureFormat format, const IVec2 &renderSize,
181                                          const Vec4 &clearColor, const std::vector<IVec4> &scissors,
182                                          const std::vector<Vec4> &scissorColors)
183 {
184     DE_ASSERT(scissors.size() == scissorColors.size());
185 
186     tcu::TextureLevel image(format, renderSize.x(), renderSize.y());
187     tcu::clear(image.getAccess(), clearColor);
188 
189     for (std::size_t i = 0; i < scissors.size(); ++i)
190     {
191         tcu::clear(
192             tcu::getSubregion(image.getAccess(), scissors[i].x(), scissors[i].y(), scissors[i].z(), scissors[i].w()),
193             scissorColors[i]);
194     }
195 
196     return image;
197 }
198 
initPrograms(SourceCollections & programCollection,const int numViewports)199 void initPrograms(SourceCollections &programCollection, const int numViewports)
200 {
201     DE_UNREF(numViewports);
202 
203     // Vertex shader
204     {
205         std::ostringstream src;
206         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
207             << "\n"
208             << "layout(location = 0) in  vec4 in_color;\n"
209             << "layout(location = 0) out vec4 out_color;\n"
210             << "\n"
211             << "void main(void)\n"
212             << "{\n"
213             << "    out_color = in_color;\n"
214             << "}\n";
215 
216         programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
217     }
218 
219     // Geometry shader
220     {
221         // Each input point generates a fullscreen quad.
222 
223         std::ostringstream src;
224         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
225             << "\n"
226             << "layout(points) in;\n"
227             << "layout(triangle_strip, max_vertices=4) out;\n"
228             << "\n"
229             << "out gl_PerVertex {\n"
230             << "    vec4 gl_Position;\n"
231             << "};\n"
232             << "\n"
233             << "layout(location = 0) in  vec4 in_color[];\n"
234             << "layout(location = 0) out vec4 out_color;\n"
235             << "\n"
236             << "void main(void)\n"
237             << "{\n"
238             << "    gl_ViewportIndex = gl_PrimitiveIDIn;\n"
239             << "    gl_Position      = vec4(-1.0, -1.0, 0.0, 1.0);\n"
240             << "    out_color        = in_color[0];\n"
241             << "    EmitVertex();"
242             << "\n"
243             << "    gl_ViewportIndex = gl_PrimitiveIDIn;\n"
244             << "    gl_Position      = vec4(-1.0, 1.0, 0.0, 1.0);\n"
245             << "    out_color        = in_color[0];\n"
246             << "    EmitVertex();"
247             << "\n"
248             << "    gl_ViewportIndex = gl_PrimitiveIDIn;\n"
249             << "    gl_Position      = vec4(1.0, -1.0, 0.0, 1.0);\n"
250             << "    out_color        = in_color[0];\n"
251             << "    EmitVertex();"
252             << "\n"
253             << "    gl_ViewportIndex = gl_PrimitiveIDIn;\n"
254             << "    gl_Position      = vec4(1.0, 1.0, 0.0, 1.0);\n"
255             << "    out_color        = in_color[0];\n"
256             << "    EmitVertex();"
257             << "}\n";
258 
259         programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
260     }
261 
262     // Fragment shader
263     {
264         std::ostringstream src;
265         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
266             << "\n"
267             << "layout(location = 0) in  vec4 in_color;\n"
268             << "layout(location = 0) out vec4 out_color;\n"
269             << "\n"
270             << "void main(void)\n"
271             << "{\n"
272             << "    out_color = in_color;\n"
273             << "}\n";
274 
275         programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
276     }
277 }
278 
279 class ScissorRenderer
280 {
281 public:
ScissorRenderer(Context & context,const IVec2 & renderSize,const int numViewports,const std::vector<IVec4> & scissors,const VkFormat colorFormat,const Vec4 & clearColor,const std::vector<Vec4> & vertices)282     ScissorRenderer(Context &context, const IVec2 &renderSize, const int numViewports,
283                     const std::vector<IVec4> &scissors, const VkFormat colorFormat, const Vec4 &clearColor,
284                     const std::vector<Vec4> &vertices)
285         : m_renderSize(renderSize)
286         , m_colorFormat(colorFormat)
287         , m_colorSubresourceRange(makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u))
288         , m_clearColor(clearColor)
289         , m_numViewports(numViewports)
290         , m_vertexBufferSize(sizeInBytes(vertices))
291     {
292         const DeviceInterface &vk       = context.getDeviceInterface();
293         const VkDevice device           = context.getDevice();
294         const uint32_t queueFamilyIndex = context.getUniversalQueueFamilyIndex();
295         Allocator &allocator            = context.getDefaultAllocator();
296 
297         m_colorImage =
298             makeImage(vk, device,
299                       makeImageCreateInfo(m_colorFormat, m_renderSize,
300                                           VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
301         m_colorImageAlloc = bindImage(vk, device, allocator, *m_colorImage, MemoryRequirement::Any);
302         m_colorAttachment =
303             makeImageView(vk, device, *m_colorImage, VK_IMAGE_VIEW_TYPE_2D, m_colorFormat, m_colorSubresourceRange);
304 
305         m_vertexBuffer      = makeBuffer(vk, device, m_vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
306         m_vertexBufferAlloc = bindBuffer(vk, device, allocator, *m_vertexBuffer, MemoryRequirement::HostVisible);
307 
308         {
309             deMemcpy(m_vertexBufferAlloc->getHostPtr(), &vertices[0], static_cast<std::size_t>(m_vertexBufferSize));
310             flushAlloc(vk, device, *m_vertexBufferAlloc);
311         }
312 
313         m_vertexModule   = createShaderModule(vk, device, context.getBinaryCollection().get("vert"), 0u);
314         m_geometryModule = createShaderModule(vk, device, context.getBinaryCollection().get("geom"), 0u);
315         m_fragmentModule = createShaderModule(vk, device, context.getBinaryCollection().get("frag"), 0u);
316         m_renderPass     = makeRenderPass(vk, device, m_colorFormat);
317         m_framebuffer =
318             makeFramebuffer(vk, device, *m_renderPass, m_colorAttachment.get(), static_cast<uint32_t>(m_renderSize.x()),
319                             static_cast<uint32_t>(m_renderSize.y()));
320         m_pipelineLayout = makePipelineLayout(vk, device);
321         m_pipeline       = makeGraphicsPipeline(vk, device, *m_pipelineLayout, *m_renderPass, *m_vertexModule,
322                                                 *m_geometryModule, *m_fragmentModule, m_renderSize, m_numViewports, scissors);
323         m_cmdPool   = createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
324         m_cmdBuffer = allocateCommandBuffer(vk, device, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
325     }
326 
draw(Context & context,const VkBuffer colorBuffer) const327     void draw(Context &context, const VkBuffer colorBuffer) const
328     {
329         const DeviceInterface &vk = context.getDeviceInterface();
330         const VkDevice device     = context.getDevice();
331         const VkQueue queue       = context.getUniversalQueue();
332 
333         beginCommandBuffer(vk, *m_cmdBuffer);
334 
335         beginRenderPass(vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer,
336                         makeRect2D(0, 0, m_renderSize.x(), m_renderSize.y()), m_clearColor);
337 
338         vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
339         {
340             const VkDeviceSize vertexBufferOffset = 0ull;
341             vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &vertexBufferOffset);
342         }
343         vk.cmdDraw(*m_cmdBuffer, static_cast<uint32_t>(m_numViewports), 1u, 0u, 0u); // one vertex per viewport
344         endRenderPass(vk, *m_cmdBuffer);
345 
346         copyImageToBuffer(vk, *m_cmdBuffer, *m_colorImage, colorBuffer, m_renderSize);
347 
348         endCommandBuffer(vk, *m_cmdBuffer);
349         submitCommandsAndWait(vk, device, queue, *m_cmdBuffer);
350     }
351 
352 private:
353     const IVec2 m_renderSize;
354     const VkFormat m_colorFormat;
355     const VkImageSubresourceRange m_colorSubresourceRange;
356     const Vec4 m_clearColor;
357     const int m_numViewports;
358     const VkDeviceSize m_vertexBufferSize;
359 
360     Move<VkImage> m_colorImage;
361     MovePtr<Allocation> m_colorImageAlloc;
362     Move<VkImageView> m_colorAttachment;
363     Move<VkBuffer> m_vertexBuffer;
364     MovePtr<Allocation> m_vertexBufferAlloc;
365     Move<VkShaderModule> m_vertexModule;
366     Move<VkShaderModule> m_geometryModule;
367     Move<VkShaderModule> m_fragmentModule;
368     Move<VkRenderPass> m_renderPass;
369     Move<VkFramebuffer> m_framebuffer;
370     Move<VkPipelineLayout> m_pipelineLayout;
371     Move<VkPipeline> m_pipeline;
372     Move<VkCommandPool> m_cmdPool;
373     Move<VkCommandBuffer> m_cmdBuffer;
374 
375     // "deleted"
376     ScissorRenderer(const ScissorRenderer &);
377     ScissorRenderer &operator=(const ScissorRenderer &);
378 };
379 
test(Context & context,const int numViewports)380 tcu::TestStatus test(Context &context, const int numViewports)
381 {
382     const DeviceInterface &vk = context.getDeviceInterface();
383     const VkDevice device     = context.getDevice();
384     Allocator &allocator      = context.getDefaultAllocator();
385 
386     const IVec2 renderSize(128, 128);
387     const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
388     const Vec4 clearColor(0.5f, 0.5f, 0.5f, 1.0f);
389     const std::vector<Vec4> vertexColors = generateColors(numViewports);
390     const std::vector<IVec4> scissors    = generateScissors(numViewports, renderSize);
391 
392     const VkDeviceSize colorBufferSize = renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
393     const Unique<VkBuffer> colorBuffer(makeBuffer(vk, device, colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT));
394     const UniquePtr<Allocation> colorBufferAlloc(
395         bindBuffer(vk, device, allocator, *colorBuffer, MemoryRequirement::HostVisible));
396 
397     zeroBuffer(vk, device, *colorBufferAlloc, colorBufferSize);
398 
399     {
400         context.getTestContext().getLog()
401             << tcu::TestLog::Message << "Rendering a colorful grid of " << numViewports << " rectangle(s)."
402             << tcu::TestLog::EndMessage << tcu::TestLog::Message << "Not covered area will be filled with a gray color."
403             << tcu::TestLog::EndMessage;
404     }
405 
406     // Draw
407     {
408         const ScissorRenderer renderer(context, renderSize, numViewports, scissors, colorFormat, clearColor,
409                                        vertexColors);
410         renderer.draw(context, *colorBuffer);
411     }
412 
413     // Log image
414     {
415         invalidateAlloc(vk, device, *colorBufferAlloc);
416 
417         const tcu::ConstPixelBufferAccess resultImage(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1u,
418                                                       colorBufferAlloc->getHostPtr());
419         const tcu::TextureLevel referenceImage =
420             generateReferenceImage(mapVkFormat(colorFormat), renderSize, clearColor, scissors, vertexColors);
421 
422         // Images should now match.
423         if (!tcu::floatThresholdCompare(context.getTestContext().getLog(), "color", "Image compare",
424                                         referenceImage.getAccess(), resultImage, Vec4(0.02f), tcu::COMPARE_LOG_RESULT))
425             return tcu::TestStatus::fail("Rendered image is not correct");
426     }
427 
428     return tcu::TestStatus::pass("OK");
429 }
430 
checkSupport(Context & context,const int)431 void checkSupport(Context &context, const int)
432 {
433     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
434     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_MULTI_VIEWPORT);
435 
436     if (context.getDeviceProperties().limits.maxViewports < MIN_MAX_VIEWPORTS)
437         TCU_THROW(NotSupportedError, "Implementation doesn't support minimum required number of viewports");
438 }
439 
440 } // namespace
441 
createScissorMultiViewportTests(tcu::TestContext & testCtx)442 tcu::TestCaseGroup *createScissorMultiViewportTests(tcu::TestContext &testCtx)
443 {
444     MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "multi_viewport"));
445 
446     for (int numViewports = 1; numViewports <= MIN_MAX_VIEWPORTS; ++numViewports)
447         addFunctionCaseWithPrograms(group.get(), "scissor_" + de::toString(numViewports), checkSupport, initPrograms,
448                                     test, numViewports);
449 
450     return group.release();
451 }
452 
453 } // namespace FragmentOperations
454 } // namespace vkt
455