1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
5 * Copyright (c) 2016 The Khronos Group Inc.
6 * Copyright (c) 2014 The Android Open Source Project
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 * \brief Scissor multi viewport tests
23 *//*--------------------------------------------------------------------*/
25 #include "vktFragmentOperationsScissorMultiViewportTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 #include "vktFragmentOperationsMakeUtil.hpp"
30 #include "vkRefUtil.hpp"
31 #include "vkTypeUtil.hpp"
32 #include "vkMemUtil.hpp"
33 #include "vkPrograms.hpp"
34 #include "vkImageUtil.hpp"
35 #include "vkQueryUtil.hpp"
36 #include "vkCmdUtil.hpp"
37 #include "vkObjUtil.hpp"
39 #include "tcuTestLog.hpp"
40 #include "tcuVector.hpp"
41 #include "tcuImageCompare.hpp"
42 #include "tcuTextureUtil.hpp"
44 #include "deUniquePtr.hpp"
49 namespace FragmentOperations
64 MIN_MAX_VIEWPORTS = 16, //!< Minimum number of viewports for an implementation supporting multiViewport.
68 inline VkDeviceSize sizeInBytes(const std::vector<T>& vec)
70 return vec.size() * sizeof(vec[0]);
73 VkImageCreateInfo makeImageCreateInfo (const VkFormat format, const IVec2& size, VkImageUsageFlags usage)
75 const VkImageCreateInfo imageParams =
77 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
78 DE_NULL, // const void* pNext;
79 (VkImageCreateFlags)0, // VkImageCreateFlags flags;
80 VK_IMAGE_TYPE_2D, // VkImageType imageType;
81 format, // VkFormat format;
82 makeExtent3D(size.x(), size.y(), 1), // VkExtent3D extent;
83 1u, // deUint32 mipLevels;
84 1u, // deUint32 arrayLayers;
85 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
86 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
87 usage, // VkImageUsageFlags usage;
88 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
89 0u, // deUint32 queueFamilyIndexCount;
90 DE_NULL, // const deUint32* pQueueFamilyIndices;
91 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
96 Move<VkPipeline> makeGraphicsPipeline (const DeviceInterface& vk,
97 const VkDevice device,
98 const VkPipelineLayout pipelineLayout,
99 const VkRenderPass renderPass,
100 const VkShaderModule vertexModule,
101 const VkShaderModule geometryModule,
102 const VkShaderModule fragmentModule,
103 const IVec2 renderSize,
104 const int numViewports,
105 const std::vector<IVec4> scissors)
107 const VkViewport defaultViewport = makeViewport(renderSize);
108 const std::vector<VkViewport> viewports(numViewports, defaultViewport);
110 DE_ASSERT(numViewports == static_cast<int>(scissors.size()));
112 std::vector<VkRect2D> rectScissors;
113 rectScissors.reserve(numViewports);
115 for (std::vector<IVec4>::const_iterator it = scissors.begin(); it != scissors.end(); ++it)
117 const VkRect2D rect =
119 makeOffset2D(it->x(), it->y()),
120 makeExtent2D(static_cast<deUint32>(it->z()), static_cast<deUint32>(it->w())),
122 rectScissors.push_back(rect);
125 return vk::makeGraphicsPipeline(vk, // const DeviceInterface& vk
126 device, // const VkDevice device
127 pipelineLayout, // const VkPipelineLayout pipelineLayout
128 vertexModule, // const VkShaderModule vertexShaderModule
129 DE_NULL, // const VkShaderModule tessellationControlModule
130 DE_NULL, // const VkShaderModule tessellationEvalModule
131 geometryModule, // const VkShaderModule geometryShaderModule
132 fragmentModule, // const VkShaderModule fragmentShaderModule
133 renderPass, // const VkRenderPass renderPass
134 viewports, // const std::vector<VkViewport>& viewports
135 rectScissors, // const std::vector<VkRect2D>& scissors
136 VK_PRIMITIVE_TOPOLOGY_POINT_LIST); // const VkPrimitiveTopology topology
139 void zeroBuffer (const DeviceInterface& vk, const VkDevice device, const Allocation& alloc, const VkDeviceSize size)
141 deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(size));
142 flushAlloc(vk, device, alloc);
145 void requireFeatureMultiViewport (const InstanceInterface& vki, const VkPhysicalDevice physDevice)
147 const VkPhysicalDeviceFeatures features = getPhysicalDeviceFeatures(vki, physDevice);
148 const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(vki, physDevice).limits;
150 if (!features.geometryShader)
151 TCU_THROW(NotSupportedError, "Required feature is not supported: geometryShader");
153 if (!features.multiViewport)
154 TCU_THROW(NotSupportedError, "Required feature is not supported: multiViewport");
156 if (limits.maxViewports < MIN_MAX_VIEWPORTS)
157 TCU_THROW(NotSupportedError, "Implementation doesn't support minimum required number of viewports");
160 std::vector<IVec4> generateScissors (const int numScissors, const IVec2& renderSize)
162 // Scissor rects will be arranged in a grid-like fashion.
164 const int numCols = deCeilFloatToInt32(deFloatSqrt(static_cast<float>(numScissors)));
165 const int numRows = deCeilFloatToInt32(static_cast<float>(numScissors) / static_cast<float>(numCols));
166 const int rectWidth = renderSize.x() / numCols;
167 const int rectHeight = renderSize.y() / numRows;
169 std::vector<IVec4> scissors;
170 scissors.reserve(numScissors);
175 for (int scissorNdx = 0; scissorNdx < numScissors; ++scissorNdx)
177 const bool nextRow = (scissorNdx != 0) && (scissorNdx % numCols == 0);
184 scissors.push_back(IVec4(x, y, rectWidth, rectHeight));
192 std::vector<Vec4> generateColors (const int numColors)
194 const Vec4 colors[] =
196 Vec4(0.18f, 0.42f, 0.17f, 1.0f),
197 Vec4(0.29f, 0.62f, 0.28f, 1.0f),
198 Vec4(0.59f, 0.84f, 0.44f, 1.0f),
199 Vec4(0.96f, 0.95f, 0.72f, 1.0f),
200 Vec4(0.94f, 0.55f, 0.39f, 1.0f),
201 Vec4(0.82f, 0.19f, 0.12f, 1.0f),
202 Vec4(0.46f, 0.15f, 0.26f, 1.0f),
203 Vec4(0.24f, 0.14f, 0.24f, 1.0f),
204 Vec4(0.49f, 0.31f, 0.26f, 1.0f),
205 Vec4(0.78f, 0.52f, 0.33f, 1.0f),
206 Vec4(0.94f, 0.82f, 0.31f, 1.0f),
207 Vec4(0.98f, 0.65f, 0.30f, 1.0f),
208 Vec4(0.22f, 0.65f, 0.53f, 1.0f),
209 Vec4(0.67f, 0.81f, 0.91f, 1.0f),
210 Vec4(0.43f, 0.44f, 0.75f, 1.0f),
211 Vec4(0.26f, 0.24f, 0.48f, 1.0f),
214 DE_ASSERT(numColors <= DE_LENGTH_OF_ARRAY(colors));
216 return std::vector<Vec4>(colors, colors + numColors);
219 //! Renders a colorful grid of rectangles.
220 tcu::TextureLevel generateReferenceImage (const tcu::TextureFormat format,
221 const IVec2& renderSize,
222 const Vec4& clearColor,
223 const std::vector<IVec4>& scissors,
224 const std::vector<Vec4>& scissorColors)
226 DE_ASSERT(scissors.size() == scissorColors.size());
228 tcu::TextureLevel image(format, renderSize.x(), renderSize.y());
229 tcu::clear(image.getAccess(), clearColor);
231 for (std::size_t i = 0; i < scissors.size(); ++i)
234 tcu::getSubregion(image.getAccess(), scissors[i].x(), scissors[i].y(), scissors[i].z(), scissors[i].w()),
241 void initPrograms (SourceCollections& programCollection, const int numViewports)
243 DE_UNREF(numViewports);
247 std::ostringstream src;
248 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
250 << "layout(location = 0) in vec4 in_color;\n"
251 << "layout(location = 0) out vec4 out_color;\n"
253 << "void main(void)\n"
255 << " out_color = in_color;\n"
258 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
263 // Each input point generates a fullscreen quad.
265 std::ostringstream src;
266 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
268 << "layout(points) in;\n"
269 << "layout(triangle_strip, max_vertices=4) out;\n"
271 << "out gl_PerVertex {\n"
272 << " vec4 gl_Position;\n"
275 << "layout(location = 0) in vec4 in_color[];\n"
276 << "layout(location = 0) out vec4 out_color;\n"
278 << "void main(void)\n"
280 << " gl_ViewportIndex = gl_PrimitiveIDIn;\n"
281 << " gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
282 << " out_color = in_color[0];\n"
285 << " gl_ViewportIndex = gl_PrimitiveIDIn;\n"
286 << " gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n"
287 << " out_color = in_color[0];\n"
290 << " gl_ViewportIndex = gl_PrimitiveIDIn;\n"
291 << " gl_Position = vec4(1.0, -1.0, 0.0, 1.0);\n"
292 << " out_color = in_color[0];\n"
295 << " gl_ViewportIndex = gl_PrimitiveIDIn;\n"
296 << " gl_Position = vec4(1.0, 1.0, 0.0, 1.0);\n"
297 << " out_color = in_color[0];\n"
301 programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
306 std::ostringstream src;
307 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
309 << "layout(location = 0) in vec4 in_color;\n"
310 << "layout(location = 0) out vec4 out_color;\n"
312 << "void main(void)\n"
314 << " out_color = in_color;\n"
317 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
321 class ScissorRenderer
324 ScissorRenderer (Context& context,
325 const IVec2& renderSize,
326 const int numViewports,
327 const std::vector<IVec4>& scissors,
328 const VkFormat colorFormat,
329 const Vec4& clearColor,
330 const std::vector<Vec4>& vertices)
331 : m_renderSize (renderSize)
332 , m_colorFormat (colorFormat)
333 , m_colorSubresourceRange (makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u))
334 , m_clearColor (clearColor)
335 , m_numViewports (numViewports)
336 , m_vertexBufferSize (sizeInBytes(vertices))
338 const DeviceInterface& vk = context.getDeviceInterface();
339 const VkDevice device = context.getDevice();
340 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
341 Allocator& allocator = context.getDefaultAllocator();
343 m_colorImage = makeImage (vk, device, makeImageCreateInfo(m_colorFormat, m_renderSize, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
344 m_colorImageAlloc = bindImage (vk, device, allocator, *m_colorImage, MemoryRequirement::Any);
345 m_colorAttachment = makeImageView (vk, device, *m_colorImage, VK_IMAGE_VIEW_TYPE_2D, m_colorFormat, m_colorSubresourceRange);
347 m_vertexBuffer = makeBuffer (vk, device, makeBufferCreateInfo(m_vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT));
348 m_vertexBufferAlloc = bindBuffer (vk, device, allocator, *m_vertexBuffer, MemoryRequirement::HostVisible);
351 deMemcpy(m_vertexBufferAlloc->getHostPtr(), &vertices[0], static_cast<std::size_t>(m_vertexBufferSize));
352 flushAlloc(vk, device, *m_vertexBufferAlloc);
355 m_vertexModule = createShaderModule (vk, device, context.getBinaryCollection().get("vert"), 0u);
356 m_geometryModule = createShaderModule (vk, device, context.getBinaryCollection().get("geom"), 0u);
357 m_fragmentModule = createShaderModule (vk, device, context.getBinaryCollection().get("frag"), 0u);
358 m_renderPass = makeRenderPass (vk, device, m_colorFormat);
359 m_framebuffer = makeFramebuffer (vk, device, *m_renderPass, m_colorAttachment.get(),
360 static_cast<deUint32>(m_renderSize.x()), static_cast<deUint32>(m_renderSize.y()));
361 m_pipelineLayout = makePipelineLayout (vk, device);
362 m_pipeline = makeGraphicsPipeline (vk, device, *m_pipelineLayout, *m_renderPass, *m_vertexModule, *m_geometryModule, *m_fragmentModule,
363 m_renderSize, m_numViewports, scissors);
364 m_cmdPool = createCommandPool (vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
365 m_cmdBuffer = allocateCommandBuffer (vk, device, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
368 void draw (Context& context, const VkBuffer colorBuffer) const
370 const DeviceInterface& vk = context.getDeviceInterface();
371 const VkDevice device = context.getDevice();
372 const VkQueue queue = context.getUniversalQueue();
374 beginCommandBuffer(vk, *m_cmdBuffer);
376 beginRenderPass(vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer, makeRect2D(0, 0, m_renderSize.x(), m_renderSize.y()), m_clearColor);
378 vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
380 const VkDeviceSize vertexBufferOffset = 0ull;
381 vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &vertexBufferOffset);
383 vk.cmdDraw(*m_cmdBuffer, static_cast<deUint32>(m_numViewports), 1u, 0u, 0u); // one vertex per viewport
384 endRenderPass(vk, *m_cmdBuffer);
386 copyImageToBuffer(vk, *m_cmdBuffer, *m_colorImage, colorBuffer, m_renderSize);
388 endCommandBuffer(vk, *m_cmdBuffer);
389 submitCommandsAndWait(vk, device, queue, *m_cmdBuffer);
393 const IVec2 m_renderSize;
394 const VkFormat m_colorFormat;
395 const VkImageSubresourceRange m_colorSubresourceRange;
396 const Vec4 m_clearColor;
397 const int m_numViewports;
398 const VkDeviceSize m_vertexBufferSize;
400 Move<VkImage> m_colorImage;
401 MovePtr<Allocation> m_colorImageAlloc;
402 Move<VkImageView> m_colorAttachment;
403 Move<VkBuffer> m_vertexBuffer;
404 MovePtr<Allocation> m_vertexBufferAlloc;
405 Move<VkShaderModule> m_vertexModule;
406 Move<VkShaderModule> m_geometryModule;
407 Move<VkShaderModule> m_fragmentModule;
408 Move<VkRenderPass> m_renderPass;
409 Move<VkFramebuffer> m_framebuffer;
410 Move<VkPipelineLayout> m_pipelineLayout;
411 Move<VkPipeline> m_pipeline;
412 Move<VkCommandPool> m_cmdPool;
413 Move<VkCommandBuffer> m_cmdBuffer;
416 ScissorRenderer (const ScissorRenderer&);
417 ScissorRenderer& operator= (const ScissorRenderer&);
420 tcu::TestStatus test (Context& context, const int numViewports)
422 requireFeatureMultiViewport(context.getInstanceInterface(), context.getPhysicalDevice());
424 const DeviceInterface& vk = context.getDeviceInterface();
425 const VkDevice device = context.getDevice();
426 Allocator& allocator = context.getDefaultAllocator();
428 const IVec2 renderSize (128, 128);
429 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
430 const Vec4 clearColor (0.5f, 0.5f, 0.5f, 1.0f);
431 const std::vector<Vec4> vertexColors = generateColors(numViewports);
432 const std::vector<IVec4> scissors = generateScissors(numViewports, renderSize);
434 const VkDeviceSize colorBufferSize = renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
435 const Unique<VkBuffer> colorBuffer (makeBuffer(vk, device, makeBufferCreateInfo(colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT)));
436 const UniquePtr<Allocation> colorBufferAlloc (bindBuffer(vk, device, allocator, *colorBuffer, MemoryRequirement::HostVisible));
438 zeroBuffer(vk, device, *colorBufferAlloc, colorBufferSize);
441 context.getTestContext().getLog()
442 << tcu::TestLog::Message << "Rendering a colorful grid of " << numViewports << " rectangle(s)." << tcu::TestLog::EndMessage
443 << tcu::TestLog::Message << "Not covered area will be filled with a gray color." << tcu::TestLog::EndMessage;
448 const ScissorRenderer renderer (context, renderSize, numViewports, scissors, colorFormat, clearColor, vertexColors);
449 renderer.draw(context, *colorBuffer);
454 invalidateAlloc(vk, device, *colorBufferAlloc);
456 const tcu::ConstPixelBufferAccess resultImage (mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1u, colorBufferAlloc->getHostPtr());
457 const tcu::TextureLevel referenceImage = generateReferenceImage(mapVkFormat(colorFormat), renderSize, clearColor, scissors, vertexColors);
459 // Images should now match.
460 if (!tcu::floatThresholdCompare(context.getTestContext().getLog(), "color", "Image compare", referenceImage.getAccess(), resultImage, Vec4(0.02f), tcu::COMPARE_LOG_RESULT))
461 return tcu::TestStatus::fail("Rendered image is not correct");
464 return tcu::TestStatus::pass("OK");
469 tcu::TestCaseGroup* createScissorMultiViewportTests (tcu::TestContext& testCtx)
471 MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "multi_viewport", ""));
473 for (int numViewports = 1; numViewports <= MIN_MAX_VIEWPORTS; ++numViewports)
474 addFunctionCaseWithPrograms(group.get(), "scissor_" + de::toString(numViewports), "", initPrograms, test, numViewports);
476 return group.release();
479 } // FragmentOperations