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