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
generateScissors(const int numScissors,const IVec2 & renderSize)139 std::vector<IVec4> generateScissors (const int numScissors, const IVec2& renderSize)
140 {
141 // Scissor rects will be arranged in a grid-like fashion.
142
143 const int numCols = deCeilFloatToInt32(deFloatSqrt(static_cast<float>(numScissors)));
144 const int numRows = deCeilFloatToInt32(static_cast<float>(numScissors) / static_cast<float>(numCols));
145 const int rectWidth = renderSize.x() / numCols;
146 const int rectHeight = renderSize.y() / numRows;
147
148 std::vector<IVec4> scissors;
149 scissors.reserve(numScissors);
150
151 int x = 0;
152 int y = 0;
153
154 for (int scissorNdx = 0; scissorNdx < numScissors; ++scissorNdx)
155 {
156 const bool nextRow = (scissorNdx != 0) && (scissorNdx % numCols == 0);
157 if (nextRow)
158 {
159 x = 0;
160 y += rectHeight;
161 }
162
163 scissors.push_back(IVec4(x, y, rectWidth, rectHeight));
164
165 x += rectWidth;
166 }
167
168 return scissors;
169 }
170
generateColors(const int numColors)171 std::vector<Vec4> generateColors (const int numColors)
172 {
173 const Vec4 colors[] =
174 {
175 Vec4(0.18f, 0.42f, 0.17f, 1.0f),
176 Vec4(0.29f, 0.62f, 0.28f, 1.0f),
177 Vec4(0.59f, 0.84f, 0.44f, 1.0f),
178 Vec4(0.96f, 0.95f, 0.72f, 1.0f),
179 Vec4(0.94f, 0.55f, 0.39f, 1.0f),
180 Vec4(0.82f, 0.19f, 0.12f, 1.0f),
181 Vec4(0.46f, 0.15f, 0.26f, 1.0f),
182 Vec4(0.24f, 0.14f, 0.24f, 1.0f),
183 Vec4(0.49f, 0.31f, 0.26f, 1.0f),
184 Vec4(0.78f, 0.52f, 0.33f, 1.0f),
185 Vec4(0.94f, 0.82f, 0.31f, 1.0f),
186 Vec4(0.98f, 0.65f, 0.30f, 1.0f),
187 Vec4(0.22f, 0.65f, 0.53f, 1.0f),
188 Vec4(0.67f, 0.81f, 0.91f, 1.0f),
189 Vec4(0.43f, 0.44f, 0.75f, 1.0f),
190 Vec4(0.26f, 0.24f, 0.48f, 1.0f),
191 };
192
193 DE_ASSERT(numColors <= DE_LENGTH_OF_ARRAY(colors));
194
195 return std::vector<Vec4>(colors, colors + numColors);
196 }
197
198 //! 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)199 tcu::TextureLevel generateReferenceImage (const tcu::TextureFormat format,
200 const IVec2& renderSize,
201 const Vec4& clearColor,
202 const std::vector<IVec4>& scissors,
203 const std::vector<Vec4>& scissorColors)
204 {
205 DE_ASSERT(scissors.size() == scissorColors.size());
206
207 tcu::TextureLevel image(format, renderSize.x(), renderSize.y());
208 tcu::clear(image.getAccess(), clearColor);
209
210 for (std::size_t i = 0; i < scissors.size(); ++i)
211 {
212 tcu::clear(
213 tcu::getSubregion(image.getAccess(), scissors[i].x(), scissors[i].y(), scissors[i].z(), scissors[i].w()),
214 scissorColors[i]);
215 }
216
217 return image;
218 }
219
initPrograms(SourceCollections & programCollection,const int numViewports)220 void initPrograms (SourceCollections& programCollection, const int numViewports)
221 {
222 DE_UNREF(numViewports);
223
224 // Vertex shader
225 {
226 std::ostringstream src;
227 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
228 << "\n"
229 << "layout(location = 0) in vec4 in_color;\n"
230 << "layout(location = 0) out vec4 out_color;\n"
231 << "\n"
232 << "void main(void)\n"
233 << "{\n"
234 << " out_color = in_color;\n"
235 << "}\n";
236
237 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
238 }
239
240 // Geometry shader
241 {
242 // Each input point generates a fullscreen quad.
243
244 std::ostringstream src;
245 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
246 << "\n"
247 << "layout(points) in;\n"
248 << "layout(triangle_strip, max_vertices=4) out;\n"
249 << "\n"
250 << "out gl_PerVertex {\n"
251 << " vec4 gl_Position;\n"
252 << "};\n"
253 << "\n"
254 << "layout(location = 0) in vec4 in_color[];\n"
255 << "layout(location = 0) out vec4 out_color;\n"
256 << "\n"
257 << "void main(void)\n"
258 << "{\n"
259 << " gl_ViewportIndex = gl_PrimitiveIDIn;\n"
260 << " gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
261 << " out_color = in_color[0];\n"
262 << " EmitVertex();"
263 << "\n"
264 << " gl_ViewportIndex = gl_PrimitiveIDIn;\n"
265 << " gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n"
266 << " out_color = in_color[0];\n"
267 << " EmitVertex();"
268 << "\n"
269 << " gl_ViewportIndex = gl_PrimitiveIDIn;\n"
270 << " gl_Position = vec4(1.0, -1.0, 0.0, 1.0);\n"
271 << " out_color = in_color[0];\n"
272 << " EmitVertex();"
273 << "\n"
274 << " gl_ViewportIndex = gl_PrimitiveIDIn;\n"
275 << " gl_Position = vec4(1.0, 1.0, 0.0, 1.0);\n"
276 << " out_color = in_color[0];\n"
277 << " EmitVertex();"
278 << "}\n";
279
280 programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
281 }
282
283 // Fragment shader
284 {
285 std::ostringstream src;
286 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
287 << "\n"
288 << "layout(location = 0) in vec4 in_color;\n"
289 << "layout(location = 0) out vec4 out_color;\n"
290 << "\n"
291 << "void main(void)\n"
292 << "{\n"
293 << " out_color = in_color;\n"
294 << "}\n";
295
296 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
297 }
298 }
299
300 class ScissorRenderer
301 {
302 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)303 ScissorRenderer (Context& context,
304 const IVec2& renderSize,
305 const int numViewports,
306 const std::vector<IVec4>& scissors,
307 const VkFormat colorFormat,
308 const Vec4& clearColor,
309 const std::vector<Vec4>& vertices)
310 : m_renderSize (renderSize)
311 , m_colorFormat (colorFormat)
312 , m_colorSubresourceRange (makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u))
313 , m_clearColor (clearColor)
314 , m_numViewports (numViewports)
315 , m_vertexBufferSize (sizeInBytes(vertices))
316 {
317 const DeviceInterface& vk = context.getDeviceInterface();
318 const VkDevice device = context.getDevice();
319 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
320 Allocator& allocator = context.getDefaultAllocator();
321
322 m_colorImage = makeImage (vk, device, makeImageCreateInfo(m_colorFormat, m_renderSize, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
323 m_colorImageAlloc = bindImage (vk, device, allocator, *m_colorImage, MemoryRequirement::Any);
324 m_colorAttachment = makeImageView (vk, device, *m_colorImage, VK_IMAGE_VIEW_TYPE_2D, m_colorFormat, m_colorSubresourceRange);
325
326 m_vertexBuffer = makeBuffer (vk, device, m_vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
327 m_vertexBufferAlloc = bindBuffer (vk, device, allocator, *m_vertexBuffer, MemoryRequirement::HostVisible);
328
329 {
330 deMemcpy(m_vertexBufferAlloc->getHostPtr(), &vertices[0], static_cast<std::size_t>(m_vertexBufferSize));
331 flushAlloc(vk, device, *m_vertexBufferAlloc);
332 }
333
334 m_vertexModule = createShaderModule (vk, device, context.getBinaryCollection().get("vert"), 0u);
335 m_geometryModule = createShaderModule (vk, device, context.getBinaryCollection().get("geom"), 0u);
336 m_fragmentModule = createShaderModule (vk, device, context.getBinaryCollection().get("frag"), 0u);
337 m_renderPass = makeRenderPass (vk, device, m_colorFormat);
338 m_framebuffer = makeFramebuffer (vk, device, *m_renderPass, m_colorAttachment.get(),
339 static_cast<deUint32>(m_renderSize.x()), static_cast<deUint32>(m_renderSize.y()));
340 m_pipelineLayout = makePipelineLayout (vk, device);
341 m_pipeline = makeGraphicsPipeline (vk, device, *m_pipelineLayout, *m_renderPass, *m_vertexModule, *m_geometryModule, *m_fragmentModule,
342 m_renderSize, m_numViewports, scissors);
343 m_cmdPool = createCommandPool (vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
344 m_cmdBuffer = allocateCommandBuffer (vk, device, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
345 }
346
draw(Context & context,const VkBuffer colorBuffer) const347 void draw (Context& context, const VkBuffer colorBuffer) const
348 {
349 const DeviceInterface& vk = context.getDeviceInterface();
350 const VkDevice device = context.getDevice();
351 const VkQueue queue = context.getUniversalQueue();
352
353 beginCommandBuffer(vk, *m_cmdBuffer);
354
355 beginRenderPass(vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer, makeRect2D(0, 0, m_renderSize.x(), m_renderSize.y()), m_clearColor);
356
357 vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
358 {
359 const VkDeviceSize vertexBufferOffset = 0ull;
360 vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &vertexBufferOffset);
361 }
362 vk.cmdDraw(*m_cmdBuffer, static_cast<deUint32>(m_numViewports), 1u, 0u, 0u); // one vertex per viewport
363 endRenderPass(vk, *m_cmdBuffer);
364
365 copyImageToBuffer(vk, *m_cmdBuffer, *m_colorImage, colorBuffer, m_renderSize);
366
367 endCommandBuffer(vk, *m_cmdBuffer);
368 submitCommandsAndWait(vk, device, queue, *m_cmdBuffer);
369 }
370
371 private:
372 const IVec2 m_renderSize;
373 const VkFormat m_colorFormat;
374 const VkImageSubresourceRange m_colorSubresourceRange;
375 const Vec4 m_clearColor;
376 const int m_numViewports;
377 const VkDeviceSize m_vertexBufferSize;
378
379 Move<VkImage> m_colorImage;
380 MovePtr<Allocation> m_colorImageAlloc;
381 Move<VkImageView> m_colorAttachment;
382 Move<VkBuffer> m_vertexBuffer;
383 MovePtr<Allocation> m_vertexBufferAlloc;
384 Move<VkShaderModule> m_vertexModule;
385 Move<VkShaderModule> m_geometryModule;
386 Move<VkShaderModule> m_fragmentModule;
387 Move<VkRenderPass> m_renderPass;
388 Move<VkFramebuffer> m_framebuffer;
389 Move<VkPipelineLayout> m_pipelineLayout;
390 Move<VkPipeline> m_pipeline;
391 Move<VkCommandPool> m_cmdPool;
392 Move<VkCommandBuffer> m_cmdBuffer;
393
394 // "deleted"
395 ScissorRenderer (const ScissorRenderer&);
396 ScissorRenderer& operator= (const ScissorRenderer&);
397 };
398
test(Context & context,const int numViewports)399 tcu::TestStatus test (Context& context, const int numViewports)
400 {
401 const DeviceInterface& vk = context.getDeviceInterface();
402 const VkDevice device = context.getDevice();
403 Allocator& allocator = context.getDefaultAllocator();
404
405 const IVec2 renderSize (128, 128);
406 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
407 const Vec4 clearColor (0.5f, 0.5f, 0.5f, 1.0f);
408 const std::vector<Vec4> vertexColors = generateColors(numViewports);
409 const std::vector<IVec4> scissors = generateScissors(numViewports, renderSize);
410
411 const VkDeviceSize colorBufferSize = renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
412 const Unique<VkBuffer> colorBuffer (makeBuffer(vk, device, colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT));
413 const UniquePtr<Allocation> colorBufferAlloc (bindBuffer(vk, device, allocator, *colorBuffer, MemoryRequirement::HostVisible));
414
415 zeroBuffer(vk, device, *colorBufferAlloc, colorBufferSize);
416
417 {
418 context.getTestContext().getLog()
419 << tcu::TestLog::Message << "Rendering a colorful grid of " << numViewports << " rectangle(s)." << tcu::TestLog::EndMessage
420 << tcu::TestLog::Message << "Not covered area will be filled with a gray color." << tcu::TestLog::EndMessage;
421 }
422
423 // Draw
424 {
425 const ScissorRenderer renderer (context, renderSize, numViewports, scissors, colorFormat, clearColor, vertexColors);
426 renderer.draw(context, *colorBuffer);
427 }
428
429 // Log image
430 {
431 invalidateAlloc(vk, device, *colorBufferAlloc);
432
433 const tcu::ConstPixelBufferAccess resultImage (mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1u, colorBufferAlloc->getHostPtr());
434 const tcu::TextureLevel referenceImage = generateReferenceImage(mapVkFormat(colorFormat), renderSize, clearColor, scissors, vertexColors);
435
436 // Images should now match.
437 if (!tcu::floatThresholdCompare(context.getTestContext().getLog(), "color", "Image compare", referenceImage.getAccess(), resultImage, Vec4(0.02f), tcu::COMPARE_LOG_RESULT))
438 return tcu::TestStatus::fail("Rendered image is not correct");
439 }
440
441 return tcu::TestStatus::pass("OK");
442 }
443
checkSupport(Context & context,const int)444 void checkSupport (Context& context, const int)
445 {
446 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
447 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_MULTI_VIEWPORT);
448
449 if (context.getDeviceProperties().limits.maxViewports < MIN_MAX_VIEWPORTS)
450 TCU_THROW(NotSupportedError, "Implementation doesn't support minimum required number of viewports");
451 }
452
453 } // anonymous
454
createScissorMultiViewportTests(tcu::TestContext & testCtx)455 tcu::TestCaseGroup* createScissorMultiViewportTests (tcu::TestContext& testCtx)
456 {
457 MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "multi_viewport", ""));
458
459 for (int numViewports = 1; numViewports <= MIN_MAX_VIEWPORTS; ++numViewports)
460 addFunctionCaseWithPrograms(group.get(), "scissor_" + de::toString(numViewports), "", checkSupport, initPrograms, test, numViewports);
461
462 return group.release();
463 }
464
465 } // FragmentOperations
466 } // vkt
467