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 #include "vktFragmentOperationsMakeUtil.hpp"
28
29 #include "vkDefs.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"
38
39 #include "tcuTestLog.hpp"
40 #include "tcuVector.hpp"
41 #include "tcuImageCompare.hpp"
42 #include "tcuTextureUtil.hpp"
43
44 #include "deUniquePtr.hpp"
45 #include "deMath.h"
46
47 namespace vkt
48 {
49 namespace FragmentOperations
50 {
51 using namespace vk;
52 using de::UniquePtr;
53 using de::MovePtr;
54 using tcu::Vec4;
55 using tcu::Vec2;
56 using tcu::IVec2;
57 using tcu::IVec4;
58
59 namespace
60 {
61
62 enum Constants
63 {
64 MIN_MAX_VIEWPORTS = 16, //!< Minimum number of viewports for an implementation supporting multiViewport.
65 };
66
67 template<typename T>
sizeInBytes(const std::vector<T> & vec)68 inline VkDeviceSize sizeInBytes(const std::vector<T>& vec)
69 {
70 return vec.size() * sizeof(vec[0]);
71 }
72
makeImageCreateInfo(const VkFormat format,const IVec2 & size,VkImageUsageFlags usage)73 VkImageCreateInfo makeImageCreateInfo (const VkFormat format, const IVec2& size, VkImageUsageFlags usage)
74 {
75 const VkImageCreateInfo imageParams =
76 {
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;
92 };
93 return imageParams;
94 }
95
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)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)
106 {
107 const VkViewport defaultViewport = makeViewport(renderSize);
108 const std::vector<VkViewport> viewports(numViewports, defaultViewport);
109
110 DE_ASSERT(numViewports == static_cast<int>(scissors.size()));
111
112 std::vector<VkRect2D> rectScissors;
113 rectScissors.reserve(numViewports);
114
115 for (std::vector<IVec4>::const_iterator it = scissors.begin(); it != scissors.end(); ++it)
116 {
117 const VkRect2D rect =
118 {
119 makeOffset2D(it->x(), it->y()),
120 makeExtent2D(static_cast<deUint32>(it->z()), static_cast<deUint32>(it->w())),
121 };
122 rectScissors.push_back(rect);
123 }
124
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
137 }
138
zeroBuffer(const DeviceInterface & vk,const VkDevice device,const Allocation & alloc,const VkDeviceSize size)139 void zeroBuffer (const DeviceInterface& vk, const VkDevice device, const Allocation& alloc, const VkDeviceSize size)
140 {
141 deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(size));
142 flushAlloc(vk, device, alloc);
143 }
144
requireFeatureMultiViewport(const InstanceInterface & vki,const VkPhysicalDevice physDevice)145 void requireFeatureMultiViewport (const InstanceInterface& vki, const VkPhysicalDevice physDevice)
146 {
147 const VkPhysicalDeviceFeatures features = getPhysicalDeviceFeatures(vki, physDevice);
148 const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(vki, physDevice).limits;
149
150 if (!features.geometryShader)
151 TCU_THROW(NotSupportedError, "Required feature is not supported: geometryShader");
152
153 if (!features.multiViewport)
154 TCU_THROW(NotSupportedError, "Required feature is not supported: multiViewport");
155
156 if (limits.maxViewports < MIN_MAX_VIEWPORTS)
157 TCU_THROW(NotSupportedError, "Implementation doesn't support minimum required number of viewports");
158 }
159
generateScissors(const int numScissors,const IVec2 & renderSize)160 std::vector<IVec4> generateScissors (const int numScissors, const IVec2& renderSize)
161 {
162 // Scissor rects will be arranged in a grid-like fashion.
163
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;
168
169 std::vector<IVec4> scissors;
170 scissors.reserve(numScissors);
171
172 int x = 0;
173 int y = 0;
174
175 for (int scissorNdx = 0; scissorNdx < numScissors; ++scissorNdx)
176 {
177 const bool nextRow = (scissorNdx != 0) && (scissorNdx % numCols == 0);
178 if (nextRow)
179 {
180 x = 0;
181 y += rectHeight;
182 }
183
184 scissors.push_back(IVec4(x, y, rectWidth, rectHeight));
185
186 x += rectWidth;
187 }
188
189 return scissors;
190 }
191
generateColors(const int numColors)192 std::vector<Vec4> generateColors (const int numColors)
193 {
194 const Vec4 colors[] =
195 {
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),
212 };
213
214 DE_ASSERT(numColors <= DE_LENGTH_OF_ARRAY(colors));
215
216 return std::vector<Vec4>(colors, colors + numColors);
217 }
218
219 //! 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)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)
225 {
226 DE_ASSERT(scissors.size() == scissorColors.size());
227
228 tcu::TextureLevel image(format, renderSize.x(), renderSize.y());
229 tcu::clear(image.getAccess(), clearColor);
230
231 for (std::size_t i = 0; i < scissors.size(); ++i)
232 {
233 tcu::clear(
234 tcu::getSubregion(image.getAccess(), scissors[i].x(), scissors[i].y(), scissors[i].z(), scissors[i].w()),
235 scissorColors[i]);
236 }
237
238 return image;
239 }
240
initPrograms(SourceCollections & programCollection,const int numViewports)241 void initPrograms (SourceCollections& programCollection, const int numViewports)
242 {
243 DE_UNREF(numViewports);
244
245 // Vertex shader
246 {
247 std::ostringstream src;
248 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
249 << "\n"
250 << "layout(location = 0) in vec4 in_color;\n"
251 << "layout(location = 0) out vec4 out_color;\n"
252 << "\n"
253 << "void main(void)\n"
254 << "{\n"
255 << " out_color = in_color;\n"
256 << "}\n";
257
258 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
259 }
260
261 // Geometry shader
262 {
263 // Each input point generates a fullscreen quad.
264
265 std::ostringstream src;
266 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
267 << "\n"
268 << "layout(points) in;\n"
269 << "layout(triangle_strip, max_vertices=4) out;\n"
270 << "\n"
271 << "out gl_PerVertex {\n"
272 << " vec4 gl_Position;\n"
273 << "};\n"
274 << "\n"
275 << "layout(location = 0) in vec4 in_color[];\n"
276 << "layout(location = 0) out vec4 out_color;\n"
277 << "\n"
278 << "void main(void)\n"
279 << "{\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"
283 << " EmitVertex();"
284 << "\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"
288 << " EmitVertex();"
289 << "\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"
293 << " EmitVertex();"
294 << "\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"
298 << " EmitVertex();"
299 << "}\n";
300
301 programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
302 }
303
304 // Fragment shader
305 {
306 std::ostringstream src;
307 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
308 << "\n"
309 << "layout(location = 0) in vec4 in_color;\n"
310 << "layout(location = 0) out vec4 out_color;\n"
311 << "\n"
312 << "void main(void)\n"
313 << "{\n"
314 << " out_color = in_color;\n"
315 << "}\n";
316
317 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
318 }
319 }
320
321 class ScissorRenderer
322 {
323 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)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))
337 {
338 const DeviceInterface& vk = context.getDeviceInterface();
339 const VkDevice device = context.getDevice();
340 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
341 Allocator& allocator = context.getDefaultAllocator();
342
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);
346
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);
349
350 {
351 deMemcpy(m_vertexBufferAlloc->getHostPtr(), &vertices[0], static_cast<std::size_t>(m_vertexBufferSize));
352 flushAlloc(vk, device, *m_vertexBufferAlloc);
353 }
354
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, 1u, &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);
366 }
367
draw(Context & context,const VkBuffer colorBuffer) const368 void draw (Context& context, const VkBuffer colorBuffer) const
369 {
370 const DeviceInterface& vk = context.getDeviceInterface();
371 const VkDevice device = context.getDevice();
372 const VkQueue queue = context.getUniversalQueue();
373
374 beginCommandBuffer(vk, *m_cmdBuffer);
375
376 beginRenderPass(vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer, makeRect2D(0, 0, m_renderSize.x(), m_renderSize.y()), m_clearColor);
377
378 vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
379 {
380 const VkDeviceSize vertexBufferOffset = 0ull;
381 vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &vertexBufferOffset);
382 }
383 vk.cmdDraw(*m_cmdBuffer, static_cast<deUint32>(m_numViewports), 1u, 0u, 0u); // one vertex per viewport
384 endRenderPass(vk, *m_cmdBuffer);
385
386 copyImageToBuffer(vk, *m_cmdBuffer, *m_colorImage, colorBuffer, m_renderSize);
387
388 endCommandBuffer(vk, *m_cmdBuffer);
389 submitCommandsAndWait(vk, device, queue, *m_cmdBuffer);
390 }
391
392 private:
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;
399
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;
414
415 // "deleted"
416 ScissorRenderer (const ScissorRenderer&);
417 ScissorRenderer& operator= (const ScissorRenderer&);
418 };
419
test(Context & context,const int numViewports)420 tcu::TestStatus test (Context& context, const int numViewports)
421 {
422 requireFeatureMultiViewport(context.getInstanceInterface(), context.getPhysicalDevice());
423
424 const DeviceInterface& vk = context.getDeviceInterface();
425 const VkDevice device = context.getDevice();
426 Allocator& allocator = context.getDefaultAllocator();
427
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);
433
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));
437
438 zeroBuffer(vk, device, *colorBufferAlloc, colorBufferSize);
439
440 {
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;
444 }
445
446 // Draw
447 {
448 const ScissorRenderer renderer (context, renderSize, numViewports, scissors, colorFormat, clearColor, vertexColors);
449 renderer.draw(context, *colorBuffer);
450 }
451
452 // Log image
453 {
454 invalidateAlloc(vk, device, *colorBufferAlloc);
455
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);
458
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");
462 }
463
464 return tcu::TestStatus::pass("OK");
465 }
466
467 } // anonymous
468
createScissorMultiViewportTests(tcu::TestContext & testCtx)469 tcu::TestCaseGroup* createScissorMultiViewportTests (tcu::TestContext& testCtx)
470 {
471 MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "multi_viewport", ""));
472
473 for (int numViewports = 1; numViewports <= MIN_MAX_VIEWPORTS; ++numViewports)
474 addFunctionCaseWithPrograms(group.get(), "scissor_" + de::toString(numViewports), "", initPrograms, test, numViewports);
475
476 return group.release();
477 }
478
479 } // FragmentOperations
480 } // vkt
481