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 tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktFragmentOperationsScissorTests.hpp"
26 #include "vktFragmentOperationsScissorMultiViewportTests.hpp"
27 #include "vktTestCaseUtil.hpp"
28 #include "vktTestGroupUtil.hpp"
29 
30 #include "vkDefs.hpp"
31 #include "vkRefUtil.hpp"
32 #include "vkTypeUtil.hpp"
33 #include "vkMemUtil.hpp"
34 #include "vkPrograms.hpp"
35 #include "vkImageUtil.hpp"
36 #include "vkCmdUtil.hpp"
37 #include "vkObjUtil.hpp"
38 
39 #include "tcuTestLog.hpp"
40 #include "tcuVector.hpp"
41 #include "tcuImageCompare.hpp"
42 
43 #include "deUniquePtr.hpp"
44 #include "deRandom.hpp"
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 //! What primitives will be drawn by the test case.
62 enum TestPrimitive
63 {
64     TEST_PRIMITIVE_POINTS,       //!< Many points.
65     TEST_PRIMITIVE_LINES,        //!< Many short lines.
66     TEST_PRIMITIVE_TRIANGLES,    //!< Many small triangles.
67     TEST_PRIMITIVE_BIG_LINE,     //!< One line crossing the whole render area.
68     TEST_PRIMITIVE_BIG_TRIANGLE, //!< One triangle covering the whole render area.
69 };
70 
71 struct VertexData
72 {
73     Vec4 position;
74     Vec4 color;
75 };
76 
77 //! Parameters used by the test case.
78 struct CaseDef
79 {
80     Vec4
81         renderArea; //!< (ox, oy, w, h), where origin (0,0) is the top-left corner of the viewport. Width and height are in range [0, 1].
82     Vec4 scissorArea; //!< scissored area (ox, oy, w, h)
83     TestPrimitive primitive;
84 };
85 
86 template <typename T>
sizeInBytes(const std::vector<T> & vec)87 inline VkDeviceSize sizeInBytes(const std::vector<T> &vec)
88 {
89     return vec.size() * sizeof(vec[0]);
90 }
91 
makeImageCreateInfo(const VkFormat format,const IVec2 & size,VkImageUsageFlags usage)92 VkImageCreateInfo makeImageCreateInfo(const VkFormat format, const IVec2 &size, VkImageUsageFlags usage)
93 {
94     const VkImageCreateInfo imageParams = {
95         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
96         DE_NULL,                             // const void* pNext;
97         (VkImageCreateFlags)0,               // VkImageCreateFlags flags;
98         VK_IMAGE_TYPE_2D,                    // VkImageType imageType;
99         format,                              // VkFormat format;
100         makeExtent3D(size.x(), size.y(), 1), // VkExtent3D extent;
101         1u,                                  // uint32_t mipLevels;
102         1u,                                  // uint32_t arrayLayers;
103         VK_SAMPLE_COUNT_1_BIT,               // VkSampleCountFlagBits samples;
104         VK_IMAGE_TILING_OPTIMAL,             // VkImageTiling tiling;
105         usage,                               // VkImageUsageFlags usage;
106         VK_SHARING_MODE_EXCLUSIVE,           // VkSharingMode sharingMode;
107         0u,                                  // uint32_t queueFamilyIndexCount;
108         DE_NULL,                             // const uint32_t* pQueueFamilyIndices;
109         VK_IMAGE_LAYOUT_UNDEFINED,           // VkImageLayout initialLayout;
110     };
111     return imageParams;
112 }
113 
makeGraphicsPipeline(const DeviceInterface & vk,const VkDevice device,const VkPipelineLayout pipelineLayout,const VkRenderPass renderPass,const VkShaderModule vertexModule,const VkShaderModule fragmentModule,const IVec2 renderSize,const IVec4 scissorArea,const VkPrimitiveTopology topology)114 Move<VkPipeline> makeGraphicsPipeline(const DeviceInterface &vk, const VkDevice device,
115                                       const VkPipelineLayout pipelineLayout, const VkRenderPass renderPass,
116                                       const VkShaderModule vertexModule, const VkShaderModule fragmentModule,
117                                       const IVec2 renderSize,
118                                       const IVec4 scissorArea, //!< (ox, oy, w, h)
119                                       const VkPrimitiveTopology topology)
120 {
121     const VkVertexInputBindingDescription vertexInputBindingDescription = {
122         0u,                          // uint32_t binding;
123         sizeof(VertexData),          // uint32_t stride;
124         VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate;
125     };
126 
127     const VkVertexInputAttributeDescription vertexInputAttributeDescriptions[] = {
128         {
129             0u,                            // uint32_t location;
130             0u,                            // uint32_t binding;
131             VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
132             0u,                            // uint32_t offset;
133         },
134         {
135             1u,                            // uint32_t location;
136             0u,                            // uint32_t binding;
137             VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
138             sizeof(Vec4),                  // uint32_t offset;
139         },
140     };
141 
142     const VkPipelineVertexInputStateCreateInfo vertexInputStateInfo = {
143         VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType                             sType;
144         DE_NULL,                                                   // const void*                                 pNext;
145         (VkPipelineVertexInputStateCreateFlags)0,                  // VkPipelineVertexInputStateCreateFlags       flags;
146         1u,                             // uint32_t                                    vertexBindingDescriptionCount;
147         &vertexInputBindingDescription, // const VkVertexInputBindingDescription*      pVertexBindingDescriptions;
148         DE_LENGTH_OF_ARRAY(
149             vertexInputAttributeDescriptions), // uint32_t                                    vertexAttributeDescriptionCount;
150         vertexInputAttributeDescriptions, // const VkVertexInputAttributeDescription*    pVertexAttributeDescriptions;
151     };
152 
153     const VkRect2D scissor = {makeOffset2D(scissorArea.x(), scissorArea.y()),
154                               makeExtent2D(scissorArea.z(), scissorArea.w())};
155 
156     const std::vector<VkViewport> viewports(1, makeViewport(renderSize));
157     const std::vector<VkRect2D> scissors(1, scissor);
158 
159     return vk::makeGraphicsPipeline(
160         vk,                     // const DeviceInterface&                        vk
161         device,                 // const VkDevice                                device
162         pipelineLayout,         // const VkPipelineLayout                        pipelineLayout
163         vertexModule,           // const VkShaderModule                          vertexShaderModule
164         DE_NULL,                // const VkShaderModule                          tessellationControlModule
165         DE_NULL,                // const VkShaderModule                          tessellationEvalModule
166         DE_NULL,                // const VkShaderModule                          geometryShaderModule
167         fragmentModule,         // const VkShaderModule                          fragmentShaderModule
168         renderPass,             // const VkRenderPass                            renderPass
169         viewports,              // const std::vector<VkViewport>&                viewports
170         scissors,               // const std::vector<VkRect2D>&                  scissors
171         topology,               // const VkPrimitiveTopology                     topology
172         0u,                     // const uint32_t                                subpass
173         0u,                     // const uint32_t                                patchControlPoints
174         &vertexInputStateInfo); // const VkPipelineVertexInputStateCreateInfo*   vertexInputStateCreateInfo
175 }
176 
makeVertex(const float x,const float y,const Vec4 & color)177 inline VertexData makeVertex(const float x, const float y, const Vec4 &color)
178 {
179     const VertexData data = {Vec4(x, y, 0.0f, 1.0f), color};
180     return data;
181 }
182 
genVertices(const TestPrimitive primitive,const Vec4 & renderArea,const Vec4 & primitiveColor)183 std::vector<VertexData> genVertices(const TestPrimitive primitive, const Vec4 &renderArea, const Vec4 &primitiveColor)
184 {
185     std::vector<VertexData> vertices;
186     de::Random rng(1234);
187 
188     const float x0   = 2.0f * renderArea.x() - 1.0f;
189     const float y0   = 2.0f * renderArea.y() - 1.0f;
190     const float rx   = 2.0f * renderArea.z();
191     const float ry   = 2.0f * renderArea.w();
192     const float size = 0.2f;
193 
194     switch (primitive)
195     {
196     case TEST_PRIMITIVE_POINTS:
197         for (int i = 0; i < 50; ++i)
198         {
199             const float x = x0 + rng.getFloat(0.0f, rx);
200             const float y = y0 + rng.getFloat(0.0f, ry);
201             vertices.push_back(makeVertex(x, y, primitiveColor));
202         }
203         break;
204 
205     case TEST_PRIMITIVE_LINES:
206         for (int i = 0; i < 30; ++i)
207         {
208             const float x = x0 + rng.getFloat(0.0f, rx - size);
209             const float y = y0 + rng.getFloat(0.0f, ry - size);
210             vertices.push_back(makeVertex(x, y, primitiveColor));
211             vertices.push_back(makeVertex(x + size, y + size, primitiveColor));
212         }
213         break;
214 
215     case TEST_PRIMITIVE_TRIANGLES:
216         for (int i = 0; i < 20; ++i)
217         {
218             const float x = x0 + rng.getFloat(0.0f, rx - size);
219             const float y = y0 + rng.getFloat(0.0f, ry - size);
220             vertices.push_back(makeVertex(x, y, primitiveColor));
221             vertices.push_back(makeVertex(x + size / 2.0f, y + size, primitiveColor));
222             vertices.push_back(makeVertex(x + size, y, primitiveColor));
223         }
224         break;
225 
226     case TEST_PRIMITIVE_BIG_LINE:
227         vertices.push_back(makeVertex(x0, y0, primitiveColor));
228         vertices.push_back(makeVertex(x0 + rx, y0 + ry, primitiveColor));
229         break;
230 
231     case TEST_PRIMITIVE_BIG_TRIANGLE:
232         vertices.push_back(makeVertex(x0, y0, primitiveColor));
233         vertices.push_back(makeVertex(x0 + rx / 2.0f, y0 + ry, primitiveColor));
234         vertices.push_back(makeVertex(x0 + rx, y0, primitiveColor));
235         break;
236     }
237 
238     return vertices;
239 }
240 
getTopology(const TestPrimitive primitive)241 VkPrimitiveTopology getTopology(const TestPrimitive primitive)
242 {
243     switch (primitive)
244     {
245     case TEST_PRIMITIVE_POINTS:
246         return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
247 
248     case TEST_PRIMITIVE_LINES:
249     case TEST_PRIMITIVE_BIG_LINE:
250         return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
251 
252     case TEST_PRIMITIVE_TRIANGLES:
253     case TEST_PRIMITIVE_BIG_TRIANGLE:
254         return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
255 
256     default:
257         DE_ASSERT(0);
258         return VK_PRIMITIVE_TOPOLOGY_LAST;
259     }
260 }
261 
262 //! Transform from normalized coords to framebuffer space.
getAreaRect(const Vec4 & area,const int width,const int height)263 inline IVec4 getAreaRect(const Vec4 &area, const int width, const int height)
264 {
265     return IVec4(static_cast<int32_t>(static_cast<float>(width) * area.x()),
266                  static_cast<int32_t>(static_cast<float>(height) * area.y()),
267                  static_cast<int32_t>(static_cast<float>(width) * area.z()),
268                  static_cast<int32_t>(static_cast<float>(height) * area.w()));
269 }
270 
applyScissor(tcu::PixelBufferAccess imageAccess,const Vec4 & floatScissorArea,const Vec4 & clearColor)271 void applyScissor(tcu::PixelBufferAccess imageAccess, const Vec4 &floatScissorArea, const Vec4 &clearColor)
272 {
273     const IVec4 scissorRect(getAreaRect(floatScissorArea, imageAccess.getWidth(), imageAccess.getHeight()));
274     const int sx0 = scissorRect.x();
275     const int sx1 = scissorRect.x() + scissorRect.z();
276     const int sy0 = scissorRect.y();
277     const int sy1 = scissorRect.y() + scissorRect.w();
278 
279     for (int y = 0; y < imageAccess.getHeight(); ++y)
280         for (int x = 0; x < imageAccess.getWidth(); ++x)
281         {
282             // Fragments outside fail the scissor test.
283             if (x < sx0 || x >= sx1 || y < sy0 || y >= sy1)
284                 imageAccess.setPixel(clearColor, x, y);
285         }
286 }
287 
initPrograms(SourceCollections & programCollection,const CaseDef caseDef)288 void initPrograms(SourceCollections &programCollection, const CaseDef caseDef)
289 {
290     DE_UNREF(caseDef);
291 
292     // Vertex shader
293     {
294         const bool usePointSize = (caseDef.primitive == TEST_PRIMITIVE_POINTS);
295 
296         std::ostringstream src;
297         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
298             << "\n"
299             << "layout(location = 0) in  vec4 in_position;\n"
300             << "layout(location = 1) in  vec4 in_color;\n"
301             << "layout(location = 0) out vec4 o_color;\n"
302             << "\n"
303             << "out gl_PerVertex {\n"
304             << "    vec4  gl_Position;\n"
305             << (usePointSize ? "    float gl_PointSize;\n" : "") << "};\n"
306             << "\n"
307             << "void main(void)\n"
308             << "{\n"
309             << "    gl_Position  = in_position;\n"
310             << (usePointSize ? "    gl_PointSize = 1.0;\n" : "") << "    o_color      = in_color;\n"
311             << "}\n";
312 
313         programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
314     }
315 
316     // Fragment shader
317     {
318         std::ostringstream src;
319         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
320             << "\n"
321             << "layout(location = 0) in  vec4 in_color;\n"
322             << "layout(location = 0) out vec4 o_color;\n"
323             << "\n"
324             << "void main(void)\n"
325             << "{\n"
326             << "    o_color = in_color;\n"
327             << "}\n";
328 
329         programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
330     }
331 }
332 
333 class ScissorRenderer
334 {
335 public:
ScissorRenderer(Context & context,const CaseDef caseDef,const IVec2 & renderSize,const VkFormat colorFormat,const Vec4 & primitiveColor,const Vec4 & clearColor)336     ScissorRenderer(Context &context, const CaseDef caseDef, const IVec2 &renderSize, const VkFormat colorFormat,
337                     const Vec4 &primitiveColor, const Vec4 &clearColor)
338         : m_renderSize(renderSize)
339         , m_colorFormat(colorFormat)
340         , m_colorSubresourceRange(makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u))
341         , m_primitiveColor(primitiveColor)
342         , m_clearColor(clearColor)
343         , m_vertices(genVertices(caseDef.primitive, caseDef.renderArea, m_primitiveColor))
344         , m_vertexBufferSize(sizeInBytes(m_vertices))
345         , m_topology(getTopology(caseDef.primitive))
346     {
347         const DeviceInterface &vk       = context.getDeviceInterface();
348         const VkDevice device           = context.getDevice();
349         const uint32_t queueFamilyIndex = context.getUniversalQueueFamilyIndex();
350         Allocator &allocator            = context.getDefaultAllocator();
351 
352         m_colorImage =
353             makeImage(vk, device,
354                       makeImageCreateInfo(m_colorFormat, m_renderSize,
355                                           VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
356         m_colorImageAlloc = bindImage(vk, device, allocator, *m_colorImage, MemoryRequirement::Any);
357         m_colorAttachment =
358             makeImageView(vk, device, *m_colorImage, VK_IMAGE_VIEW_TYPE_2D, m_colorFormat, m_colorSubresourceRange);
359 
360         m_vertexBuffer      = makeBuffer(vk, device, m_vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
361         m_vertexBufferAlloc = bindBuffer(vk, device, allocator, *m_vertexBuffer, MemoryRequirement::HostVisible);
362 
363         {
364             deMemcpy(m_vertexBufferAlloc->getHostPtr(), &m_vertices[0], static_cast<std::size_t>(m_vertexBufferSize));
365             flushAlloc(vk, device, *m_vertexBufferAlloc);
366         }
367 
368         m_vertexModule   = createShaderModule(vk, device, context.getBinaryCollection().get("vert"), 0u);
369         m_fragmentModule = createShaderModule(vk, device, context.getBinaryCollection().get("frag"), 0u);
370         m_renderPass     = makeRenderPass(vk, device, m_colorFormat);
371         m_framebuffer =
372             makeFramebuffer(vk, device, *m_renderPass, m_colorAttachment.get(), static_cast<uint32_t>(m_renderSize.x()),
373                             static_cast<uint32_t>(m_renderSize.y()));
374         m_pipelineLayout = makePipelineLayout(vk, device);
375         m_cmdPool   = createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
376         m_cmdBuffer = allocateCommandBuffer(vk, device, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
377     }
378 
draw(Context & context,const Vec4 & scissorAreaFloat,const VkBuffer colorBuffer) const379     void draw(Context &context, const Vec4 &scissorAreaFloat, const VkBuffer colorBuffer) const
380     {
381         const DeviceInterface &vk = context.getDeviceInterface();
382         const VkDevice device     = context.getDevice();
383         const VkQueue queue       = context.getUniversalQueue();
384 
385         // New pipeline, because we're modifying scissor (we don't use dynamic state).
386         const Unique<VkPipeline> pipeline(makeGraphicsPipeline(
387             vk, device, *m_pipelineLayout, *m_renderPass, *m_vertexModule, *m_fragmentModule, m_renderSize,
388             getAreaRect(scissorAreaFloat, m_renderSize.x(), m_renderSize.y()), m_topology));
389 
390         beginCommandBuffer(vk, *m_cmdBuffer);
391 
392         beginRenderPass(vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer,
393                         makeRect2D(0, 0, m_renderSize.x(), m_renderSize.y()), m_clearColor);
394 
395         vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
396         {
397             const VkDeviceSize vertexBufferOffset = 0ull;
398             vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &vertexBufferOffset);
399         }
400 
401         vk.cmdDraw(*m_cmdBuffer, static_cast<uint32_t>(m_vertices.size()), 1u, 0u, 0u);
402         endRenderPass(vk, *m_cmdBuffer);
403 
404         copyImageToBuffer(vk, *m_cmdBuffer, *m_colorImage, colorBuffer, m_renderSize);
405 
406         endCommandBuffer(vk, *m_cmdBuffer);
407         submitCommandsAndWait(vk, device, queue, *m_cmdBuffer);
408         context.resetCommandPoolForVKSC(device, *m_cmdPool);
409     }
410 
411 private:
412     const IVec2 m_renderSize;
413     const VkFormat m_colorFormat;
414     const VkImageSubresourceRange m_colorSubresourceRange;
415     const Vec4 m_primitiveColor;
416     const Vec4 m_clearColor;
417     const std::vector<VertexData> m_vertices;
418     const VkDeviceSize m_vertexBufferSize;
419     const VkPrimitiveTopology m_topology;
420 
421     Move<VkImage> m_colorImage;
422     MovePtr<Allocation> m_colorImageAlloc;
423     Move<VkImageView> m_colorAttachment;
424     Move<VkBuffer> m_vertexBuffer;
425     MovePtr<Allocation> m_vertexBufferAlloc;
426     Move<VkShaderModule> m_vertexModule;
427     Move<VkShaderModule> m_fragmentModule;
428     Move<VkRenderPass> m_renderPass;
429     Move<VkFramebuffer> m_framebuffer;
430     Move<VkPipelineLayout> m_pipelineLayout;
431     Move<VkCommandPool> m_cmdPool;
432     Move<VkCommandBuffer> m_cmdBuffer;
433 
434     // "deleted"
435     ScissorRenderer(const ScissorRenderer &);
436     ScissorRenderer &operator=(const ScissorRenderer &);
437 };
438 
test(Context & context,const CaseDef caseDef)439 tcu::TestStatus test(Context &context, const CaseDef caseDef)
440 {
441     const DeviceInterface &vk = context.getDeviceInterface();
442     const VkDevice device     = context.getDevice();
443     Allocator &allocator      = context.getDefaultAllocator();
444 
445     const IVec2 renderSize(128, 128);
446     const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
447     const Vec4 scissorFullArea(0.0f, 0.0f, 1.0f, 1.0f);
448     const Vec4 primitiveColor(1.0f, 1.0f, 1.0f, 1.0f);
449     const Vec4 clearColor(0.5f, 0.5f, 1.0f, 1.0f);
450 
451     const VkDeviceSize colorBufferSize = renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
452     const Unique<VkBuffer> colorBufferFull(makeBuffer(vk, device, colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT));
453     const UniquePtr<Allocation> colorBufferFullAlloc(
454         bindBuffer(vk, device, allocator, *colorBufferFull, MemoryRequirement::HostVisible));
455 
456     const Unique<VkBuffer> colorBufferScissored(
457         makeBuffer(vk, device, colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT));
458     const UniquePtr<Allocation> colorBufferScissoredAlloc(
459         bindBuffer(vk, device, allocator, *colorBufferScissored, MemoryRequirement::HostVisible));
460 
461     zeroBuffer(vk, device, *colorBufferFullAlloc, colorBufferSize);
462     zeroBuffer(vk, device, *colorBufferScissoredAlloc, colorBufferSize);
463 
464     // Draw
465     {
466         const ScissorRenderer renderer(context, caseDef, renderSize, colorFormat, primitiveColor, clearColor);
467 
468         renderer.draw(context, scissorFullArea, *colorBufferFull);
469         renderer.draw(context, caseDef.scissorArea, *colorBufferScissored);
470     }
471 
472     // Log image
473     {
474         invalidateAlloc(vk, device, *colorBufferFullAlloc);
475         invalidateAlloc(vk, device, *colorBufferScissoredAlloc);
476 
477         const tcu::ConstPixelBufferAccess resultImage(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1u,
478                                                       colorBufferScissoredAlloc->getHostPtr());
479         tcu::PixelBufferAccess referenceImage(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1u,
480                                               colorBufferFullAlloc->getHostPtr());
481 
482         // Apply scissor to the full image, so we can compare it with the result image.
483         applyScissor(referenceImage, caseDef.scissorArea, clearColor);
484 
485         // Images should now match.
486         if (!tcu::floatThresholdCompare(context.getTestContext().getLog(), "color", "Image compare", referenceImage,
487                                         resultImage, Vec4(0.02f), tcu::COMPARE_LOG_RESULT))
488             return tcu::TestStatus::fail("Rendered image is not correct");
489     }
490 
491     return tcu::TestStatus::pass("OK");
492 }
493 
494 //! \note The ES 2.0 scissoring tests included color/depth/stencil clear cases, but these operations are not affected by scissor test in Vulkan.
495 //!       Scissor is part of the pipeline state and pipeline only affects the drawing commands.
createTestsInGroup(tcu::TestCaseGroup * scissorGroup)496 void createTestsInGroup(tcu::TestCaseGroup *scissorGroup)
497 {
498     tcu::TestContext &testCtx = scissorGroup->getTestContext();
499 
500     struct TestSpec
501     {
502         const char *name;
503         CaseDef caseDef;
504     };
505 
506     const Vec4 areaFull(0.0f, 0.0f, 1.0f, 1.0f);
507     const Vec4 areaCropped(0.2f, 0.2f, 0.6f, 0.6f);
508     const Vec4 areaCroppedMore(0.4f, 0.4f, 0.2f, 0.2f);
509     const Vec4 areaLeftHalf(0.0f, 0.0f, 0.5f, 1.0f);
510     const Vec4 areaRightHalf(0.5f, 0.0f, 0.5f, 1.0f);
511 
512     // Points
513     {
514         MovePtr<tcu::TestCaseGroup> primitiveGroup(new tcu::TestCaseGroup(testCtx, "points"));
515 
516         const TestSpec cases[] = {
517             // Points fully inside the scissor area
518             {"inside", {areaFull, areaFull, TEST_PRIMITIVE_POINTS}},
519             // Points partially inside the scissor area
520             {"partially_inside", {areaFull, areaCropped, TEST_PRIMITIVE_POINTS}},
521             // Points fully outside the scissor area
522             {"outside", {areaLeftHalf, areaRightHalf, TEST_PRIMITIVE_POINTS}},
523         };
524 
525         for (int i = 0; i < DE_LENGTH_OF_ARRAY(cases); ++i)
526             addFunctionCaseWithPrograms(primitiveGroup.get(), cases[i].name, initPrograms, test, cases[i].caseDef);
527 
528         scissorGroup->addChild(primitiveGroup.release());
529     }
530 
531     // Lines
532     {
533         MovePtr<tcu::TestCaseGroup> primitiveGroup(new tcu::TestCaseGroup(testCtx, "lines"));
534 
535         const TestSpec cases[] = {
536             // Lines fully inside the scissor area
537             {"inside", {areaFull, areaFull, TEST_PRIMITIVE_LINES}},
538             // Lines partially inside the scissor area
539             {"partially_inside", {areaFull, areaCropped, TEST_PRIMITIVE_LINES}},
540             // Lines fully outside the scissor area
541             {"outside", {areaLeftHalf, areaRightHalf, TEST_PRIMITIVE_LINES}},
542             // A line crossing the scissor area
543             {"crossing", {areaFull, areaCroppedMore, TEST_PRIMITIVE_BIG_LINE}},
544         };
545 
546         for (int i = 0; i < DE_LENGTH_OF_ARRAY(cases); ++i)
547             addFunctionCaseWithPrograms(primitiveGroup.get(), cases[i].name, initPrograms, test, cases[i].caseDef);
548 
549         scissorGroup->addChild(primitiveGroup.release());
550     }
551 
552     // Triangles
553     {
554         MovePtr<tcu::TestCaseGroup> primitiveGroup(new tcu::TestCaseGroup(testCtx, "triangles"));
555 
556         const TestSpec cases[] = {
557             // Triangles fully inside the scissor area
558             {"inside", {areaFull, areaFull, TEST_PRIMITIVE_TRIANGLES}},
559             // Triangles partially inside the scissor area
560             {"partially_inside", {areaFull, areaCropped, TEST_PRIMITIVE_TRIANGLES}},
561             // Triangles fully outside the scissor area
562             {"outside", {areaLeftHalf, areaRightHalf, TEST_PRIMITIVE_TRIANGLES}},
563             // A triangle crossing the scissor area
564             {"crossing", {areaFull, areaCroppedMore, TEST_PRIMITIVE_BIG_TRIANGLE}},
565         };
566 
567         for (int i = 0; i < DE_LENGTH_OF_ARRAY(cases); ++i)
568             addFunctionCaseWithPrograms(primitiveGroup.get(), cases[i].name, initPrograms, test, cases[i].caseDef);
569 
570         scissorGroup->addChild(primitiveGroup.release());
571     }
572 
573     // Mulit-viewport scissor
574     {
575         scissorGroup->addChild(createScissorMultiViewportTests(testCtx));
576     }
577 }
578 
579 } // namespace
580 
createScissorTests(tcu::TestContext & testCtx)581 tcu::TestCaseGroup *createScissorTests(tcu::TestContext &testCtx)
582 {
583     return createTestGroup(testCtx, "scissor", createTestsInGroup);
584 }
585 
586 } // namespace FragmentOperations
587 } // namespace vkt
588